Merge "Minor changes"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 260ee8d..f54f132 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -56,6 +56,9 @@
         },
         {
           "include-filter": "*RefreshRateOverlayTest.*"
+        },
+        {
+          "exclude-filter": "*ChildLayerTest#ChildrenSurviveParentDestruction"
         }
       ]
     },
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 6fb9a4d..48d48ac 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -193,8 +193,9 @@
         { OPT,      "events/ext4/ext4_da_write_end/enable" },
         { OPT,      "events/ext4/ext4_sync_file_enter/enable" },
         { OPT,      "events/ext4/ext4_sync_file_exit/enable" },
-        { REQ,      "events/block/block_rq_issue/enable" },
-        { REQ,      "events/block/block_rq_complete/enable" },
+        { OPT,      "events/block/block_bio_queue/enable" },
+        { OPT,      "events/block/block_bio_complete/enable" },
+        { OPT,      "events/ufs/ufshcd_command/enable" },
     } },
     { "mmc",        "eMMC commands",    0, {
         { REQ,      "events/mmc/enable" },
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index 9186514..fa7be18 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -18,3 +18,9 @@
     chmod 0666 /sys/kernel/tracing/events/filemap/enable
     chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable
 
+    # Allow traced_probes to use the raw_syscall filters to trace only a subset
+    # of syscalls.
+    chmod 0666 /sys/kernel/tracing/events/raw_syscalls/sys_enter/filter
+    chmod 0666 /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/filter
+    chmod 0666 /sys/kernel/tracing/events/raw_syscalls/sys_exit/filter
+    chmod 0666 /sys/kernel/debug/tracing/events/raw_syscalls/sys_exit/filter
diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md
index eb0d898..3606827 100644
--- a/cmds/bugreportz/readme.md
+++ b/cmds/bugreportz/readme.md
@@ -1,6 +1,6 @@
 # bugreportz protocol
 
-`bugreportz` is used to generate a zippped bugreport whose path is passed back to `adb`, using
+`bugreportz` is used to generate a zipped bugreport whose path is passed back to `adb`, using
 the simple protocol defined below.
 
 # Version 1.1
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a60972b..a62bd01 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -126,6 +126,7 @@
     ],
     required: [
         "atrace",
+        "bugreport_procdump",
         "dmabuf_dump",
         "ip",
         "iptables",
diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING
index 839a2c3..649a13e 100644
--- a/cmds/dumpstate/TEST_MAPPING
+++ b/cmds/dumpstate/TEST_MAPPING
@@ -9,15 +9,15 @@
       ]
     },
     {
-      "name": "dumpstate_smoke_test"
-    },
-    {
       "name": "dumpstate_test"
     }
   ],
   "postsubmit": [
     {
       "name": "BugreportManagerTestCases"
+    },
+    {
+      "name": "dumpstate_smoke_test"
     }
   ],
   "imports": [
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 6dea91b..b076c15 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -183,6 +183,7 @@
 #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
 #define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
 #define CGROUPFS_DIR "/sys/fs/cgroup"
+#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
 
 // TODO(narayan): Since this information has to be kept in sync
 // with tombstoned, we should just put it in a common header.
@@ -233,6 +234,7 @@
 // task and the log title of the duration report.
 static const std::string DUMP_TRACES_TASK = "DUMP TRACES";
 static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT";
+static const std::string DUMP_NETSTATS_PROTO_TASK = "DUMP NETSTATS PROTO";
 static const std::string DUMP_HALS_TASK = "DUMP HALS";
 static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
 static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
@@ -765,7 +767,7 @@
 }
 
 void Dumpstate::PrintHeader() const {
-    std::string build, fingerprint, radio, bootloader, network;
+    std::string build, fingerprint, radio, bootloader, network, sdkversion;
     char date[80];
 
     build = android::base::GetProperty("ro.build.display.id", "(unknown)");
@@ -773,6 +775,7 @@
     radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
     bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
     network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
+    sdkversion = android::base::GetProperty("ro.build.version.sdk", "(unknown)");
     strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));
 
     printf("========================================================\n");
@@ -790,9 +793,10 @@
     if (module_metadata_version != 0) {
         printf("Module Metadata version: %" PRId64 "\n", module_metadata_version);
     }
-    printf("SDK extension versions [r=%s s=%s]\n",
-           android::base::GetProperty("build.version.extensions.r", "-").c_str(),
-           android::base::GetProperty("build.version.extensions.s", "-").c_str());
+    printf("Android SDK version: %s\n", sdkversion.c_str());
+    printf("SDK extensions: ");
+    RunCommandToFd(STDOUT_FILENO, "", {SDK_EXT_INFO, "--header"},
+                   CommandOptions::WithTimeout(1).Always().DropRoot().Build());
 
     printf("Kernel: ");
     DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
@@ -1025,7 +1029,7 @@
         MYLOGE("Could not open %s to dump incident report.\n", path.c_str());
         return;
     }
-    RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(120).Build());
+    RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(20).Build());
     bool empty = 0 == lseek(fd, 0, SEEK_END);
     if (!empty) {
         // Use a different name from "incident.proto"
@@ -1038,6 +1042,26 @@
     }
 }
 
+static void DumpNetstatsProto() {
+    const std::string path = ds.bugreport_internal_dir_ + "/tmp_netstats_proto";
+    auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
+                O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+    if (fd < 0) {
+        MYLOGE("Could not open %s to dump netstats proto.\n", path.c_str());
+        return;
+    }
+    RunCommandToFd(fd, "", {"dumpsys", "netstats", "--proto"},
+            CommandOptions::WithTimeout(5).Build());
+    bool empty = 0 == lseek(fd, 0, SEEK_END);
+    if (!empty) {
+        ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "netstats" + kProtoExt,
+                path);
+    } else {
+        unlink(path.c_str());
+    }
+}
+
 static void MaybeAddSystemTraceToZip() {
     // This function copies into the .zip the system trace that was snapshotted
     // by the early call to MaybeSnapshotSystemTrace(), if any background
@@ -1063,7 +1087,7 @@
         return;
     }
     RunCommandToFd(fd, "", {"cmd", "window", "dump-visible-window-views"},
-                   CommandOptions::WithTimeout(120).Build());
+                   CommandOptions::WithTimeout(10).Build());
     bool empty = 0 == lseek(fd, 0, SEEK_END);
     if (!empty) {
         ds.AddZipEntry("visible_windows.zip", path);
@@ -1402,7 +1426,9 @@
 // Dump all of the files that make up the vendor interface.
 // See the files listed in dumpFileList() for the latest list of files.
 static void DumpVintf() {
-    const auto vintfFiles = android::vintf::details::dumpFileList();
+
+    const std::string sku = android::base::GetProperty("ro.boot.product.hardware.sku", "");
+    const auto vintfFiles = android::vintf::details::dumpFileList(sku);
     for (const auto vintfFile : vintfFiles) {
         struct stat st;
         if (stat(vintfFile.c_str(), &st) == 0) {
@@ -1571,7 +1597,8 @@
     DurationReporter duration_reporter("DUMPSTATE");
 
     // Enqueue slow functions into the thread pool, if the parallel run is enabled.
-    std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins;
+    std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins,
+            dump_netstats_report;
     if (ds.dump_pool_) {
         // Pool was shutdown in DumpstateDefaultAfterCritical method in order to
         // drop root user. Restarts it with two threads for the parallel run.
@@ -1580,6 +1607,8 @@
         dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
         dump_incident_report = ds.dump_pool_->enqueueTask(
             DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
+        dump_netstats_report = ds.dump_pool_->enqueueTask(
+            DUMP_NETSTATS_PROTO_TASK, &DumpNetstatsProto);
         dump_board = ds.dump_pool_->enqueueTaskWithFd(
             DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
         dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
@@ -1595,7 +1624,8 @@
     RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
                             "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
 
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20);
+    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "BUGREPORT_PROCDUMP", {"bugreport_procdump"},
+                                         CommandOptions::AS_ROOT);
 
     RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpVisibleWindowViews);
 
@@ -1612,9 +1642,6 @@
     RunCommand("PROCESSES AND THREADS",
                {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"});
 
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"},
-                                         CommandOptions::AS_ROOT);
-
     if (ds.dump_pool_) {
         WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_hals));
     } else {
@@ -1644,8 +1671,6 @@
 
     RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
 
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES");
-
     for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
     for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
 
@@ -1778,6 +1803,13 @@
     dump_frozen_cgroupfs();
 
     if (ds.dump_pool_) {
+        WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_netstats_report));
+    } else {
+        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_NETSTATS_PROTO_TASK,
+                DumpNetstatsProto);
+    }
+
+    if (ds.dump_pool_) {
         WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_incident_report));
     } else {
         RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK,
@@ -1859,6 +1891,9 @@
     DumpFile("PSI memory", "/proc/pressure/memory");
     DumpFile("PSI io", "/proc/pressure/io");
 
+    RunCommand("SDK EXTENSIONS", {SDK_EXT_INFO, "--dump"},
+               CommandOptions::WithTimeout(10).Always().DropRoot().Build());
+
     if (dump_pool_) {
         RETURN_IF_USER_DENIED_CONSENT();
         WaitForTask(std::move(dump_traces));
@@ -3883,15 +3918,6 @@
     return;
 }
 
-void do_showmap(int pid, const char *name) {
-    char title[255];
-    char arg[255];
-
-    snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name);
-    snprintf(arg, sizeof(arg), "%d", pid);
-    RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT);
-}
-
 int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
     DurationReporter duration_reporter(title);
 
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index ee6b1ae..66f84cb 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -619,9 +619,6 @@
 /* Displays a processes times */
 void show_showtime(int pid, const char *name);
 
-/* Runs "showmap" for a process */
-void do_showmap(int pid, const char *name);
-
 /* Gets the dmesg output for the kernel */
 void do_dmesg();
 
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 01f7d30..c163095 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -35,9 +35,12 @@
 GLHelper::~GLHelper() {
 }
 
-bool GLHelper::setUp(const ShaderDesc* shaderDescs, size_t numShaders) {
+bool GLHelper::setUp(const sp<IBinder>& displayToken, const ShaderDesc* shaderDescs,
+                     size_t numShaders) {
     bool result;
 
+    mDisplayToken = displayToken;
+
     mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     if (mDisplay == EGL_NO_DISPLAY) {
         fprintf(stderr, "eglGetDisplay error: %#x\n", eglGetError());
@@ -221,14 +224,8 @@
 }
 
 bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) {
-    const sp<IBinder> dpy = mSurfaceComposerClient->getInternalDisplayToken();
-    if (dpy == nullptr) {
-        fprintf(stderr, "SurfaceComposer::getInternalDisplayToken failed.\n");
-        return false;
-    }
-
     ui::DisplayMode mode;
-    status_t err = mSurfaceComposerClient->getActiveDisplayMode(dpy, &mode);
+    status_t err = mSurfaceComposerClient->getActiveDisplayMode(mDisplayToken, &mode);
     if (err != NO_ERROR) {
         fprintf(stderr, "SurfaceComposer::getActiveDisplayMode failed: %#x\n", err);
         return false;
diff --git a/cmds/flatland/GLHelper.h b/cmds/flatland/GLHelper.h
index d09463a..5194f50 100644
--- a/cmds/flatland/GLHelper.h
+++ b/cmds/flatland/GLHelper.h
@@ -44,7 +44,7 @@
 
     ~GLHelper();
 
-    bool setUp(const ShaderDesc* shaderDescs, size_t numShaders);
+    bool setUp(const sp<IBinder>& displayToken, const ShaderDesc* shaderDescs, size_t numShaders);
 
     void tearDown();
 
@@ -87,6 +87,8 @@
     size_t mNumShaders;
 
     GLuint mDitherTexture;
+
+    sp<IBinder> mDisplayToken;
 };
 
 } // namespace android
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
index 7ceb397..6d14d56 100644
--- a/cmds/flatland/Main.cpp
+++ b/cmds/flatland/Main.cpp
@@ -20,6 +20,7 @@
 #include <gui/SurfaceControl.h>
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
 #include <ui/Fence.h>
 #include <utils/Trace.h>
 
@@ -34,9 +35,10 @@
 
 using namespace ::android;
 
-static uint32_t g_SleepBetweenSamplesMs = 0;
-static bool     g_PresentToWindow       = false;
-static size_t   g_BenchmarkNameLen      = 0;
+static uint32_t    g_SleepBetweenSamplesMs = 0;
+static bool        g_PresentToWindow       = false;
+static size_t      g_BenchmarkNameLen      = 0;
+static sp<IBinder> g_DisplayToken          = nullptr;
 
 struct BenchmarkDesc {
     // The name of the test.
@@ -393,7 +395,7 @@
         uint32_t h = mDesc.runHeights[mInstance];
 
         mGLHelper = new GLHelper();
-        result = mGLHelper->setUp(shaders, NELEMS(shaders));
+        result = mGLHelper->setUp(g_DisplayToken, shaders, NELEMS(shaders));
         if (!result) {
             return false;
         }
@@ -718,13 +720,17 @@
 }
 
 // Print the command usage help to stderr.
-static void showHelp(const char *cmd) {
-    fprintf(stderr, "usage: %s [options]\n", cmd);
-    fprintf(stderr, "options include:\n"
-                    "  -s N            sleep for N ms between samples\n"
-                    "  -d              display the test frame to a window\n"
-                    "  --help          print this helpful message and exit\n"
-            );
+static void showHelp(const char* cmd) {
+  fprintf(stderr, "usage: %s [options]\n", cmd);
+  fprintf(
+      stderr,
+      "options include:\n"
+      "  -s N            sleep for N ms between samples\n"
+      "  -d              display the test frame to a window\n"
+      "  -i display-id   specify a display ID to use for multi-display device\n"
+      "                  see \"dumpsys SurfaceFlinger --display-id\" for valid "
+      "display IDs\n"
+      "  --help          print this helpful message and exit\n");
 }
 
 int main(int argc, char** argv) {
@@ -733,6 +739,14 @@
         exit(0);
     }
 
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    if (ids.empty()) {
+        fprintf(stderr, "Failed to get ID for any displays.\n");
+        exit(3);
+    }
+
+    std::optional<PhysicalDisplayId> displayId;
+
     for (;;) {
         int ret;
         int option_index = 0;
@@ -741,7 +755,7 @@
             {     0,               0, 0,  0 }
         };
 
-        ret = getopt_long(argc, argv, "ds:",
+        ret = getopt_long(argc, argv, "ds:i:",
                           long_options, &option_index);
 
         if (ret < 0) {
@@ -757,6 +771,14 @@
                 g_SleepBetweenSamplesMs = atoi(optarg);
             break;
 
+            case 'i':
+                displayId = DisplayId::fromValue<PhysicalDisplayId>(atoll(optarg));
+                if (!displayId) {
+                    fprintf(stderr, "Invalid display ID: %s.\n", optarg);
+                    exit(4);
+                }
+            break;
+
             case 0:
                 if (strcmp(long_options[option_index].name, "help")) {
                     showHelp(argv[0]);
@@ -770,6 +792,22 @@
         }
     }
 
+    if (!displayId) { // no display id is specified
+        if (ids.size() == 1) {
+            displayId = ids.front();
+        } else {
+            fprintf(stderr, "Please specify a display ID for multi-display device.\n");
+            showHelp(argv[0]);
+            exit(5);
+        }
+    }
+
+    g_DisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+    if (g_DisplayToken == nullptr) {
+        fprintf(stderr, "SurfaceComposer::getPhysicalDisplayToken failed.\n");
+        exit(6);
+    }
+
     g_BenchmarkNameLen = maxBenchmarkNameLen();
 
     printf(" cmdline:");
@@ -782,4 +820,6 @@
         fprintf(stderr, "exiting due to error.\n");
         return 1;
     }
+
+    return 0;
 }
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 0f7c489..ac101ec 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -25,6 +25,7 @@
         "CrateManager.cpp",
         "InstalldNativeService.cpp",
         "QuotaUtils.cpp",
+        "SysTrace.cpp",
         "dexopt.cpp",
         "execv_helper.cpp",
         "globals.cpp",
@@ -173,7 +174,7 @@
 
     // Needs to be wherever installd is as it's execed by
     // installd.
-    required: ["migrate_legacy_obb_data.sh"],
+    required: ["migrate_legacy_obb_data"],
 }
 
 // OTA chroot tool
@@ -299,6 +300,6 @@
 
 // Script to migrate legacy obb data.
 sh_binary {
-    name: "migrate_legacy_obb_data.sh",
+    name: "migrate_legacy_obb_data",
     src: "migrate_legacy_obb_data.sh",
 }
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 1d7dd5f..b1283eb 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -16,8 +16,6 @@
 
 #include "InstalldNativeService.h"
 
-#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
-
 #include <errno.h>
 #include <fts.h>
 #include <inttypes.h>
@@ -75,6 +73,7 @@
 #include "CrateManager.h"
 #include "MatchExtensionGen.h"
 #include "QuotaUtils.h"
+#include "SysTrace.h"
 
 #ifndef LOG_TAG
 #define LOG_TAG "installd"
@@ -100,6 +99,8 @@
 
 static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
 static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+static constexpr const char* kMiscMirrorCePath = "/data_mirror/misc_ce";
+static constexpr const char* kMiscMirrorDePath = "/data_mirror/misc_de";
 
 static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
 
@@ -126,8 +127,6 @@
 
 namespace {
 
-constexpr const char* kDump = "android.permission.DUMP";
-
 static binder::Status ok() {
     return binder::Status::ok();
 }
@@ -151,19 +150,6 @@
     return binder::Status::fromServiceSpecificError(code, String8(msg.c_str()));
 }
 
-binder::Status checkPermission(const char* permission) {
-    pid_t pid;
-    uid_t uid;
-
-    if (checkCallingPermission(String16(permission), reinterpret_cast<int32_t*>(&pid),
-            reinterpret_cast<int32_t*>(&uid))) {
-        return ok();
-    } else {
-        return exception(binder::Status::EX_SECURITY,
-                StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission));
-    }
-}
-
 binder::Status checkUid(uid_t expectedUid) {
     uid_t uid = IPCThreadState::self()->getCallingUid();
     if (uid == expectedUid || uid == AID_ROOT) {
@@ -230,6 +216,19 @@
     }
 }
 
+binder::Status checkArgumentFileName(const std::string& path) {
+    if (path.empty()) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Missing name");
+    }
+    for (const char& c : path) {
+        if (c == '\0' || c == '\n' || c == '/') {
+            return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                             StringPrintf("Name %s is malformed", path.c_str()));
+        }
+    }
+    return ok();
+}
+
 #define ENFORCE_UID(uid) {                                  \
     binder::Status status = checkUid((uid));                \
     if (!status.isOk()) {                                   \
@@ -266,6 +265,14 @@
     }                                                       \
 }
 
+#define CHECK_ARGUMENT_FILE_NAME(path)                         \
+    {                                                          \
+        binder::Status status = checkArgumentFileName((path)); \
+        if (!status.isOk()) {                                  \
+            return status;                                     \
+        }                                                      \
+    }
+
 #ifdef GRANULAR_LOCKS
 
 /**
@@ -380,13 +387,7 @@
     return android::OK;
 }
 
-status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) {
-    const binder::Status dump_permission = checkPermission(kDump);
-    if (!dump_permission.isOk()) {
-        dprintf(fd, "%s\n", dump_permission.toString8().c_str());
-        return PERMISSION_DENIED;
-    }
-
+status_t InstalldNativeService::dump(int fd, const Vector<String16>& /* args */) {
     {
         std::lock_guard<std::recursive_mutex> lock(mMountsLock);
         dprintf(fd, "Storage mounts:\n");
@@ -1006,6 +1007,12 @@
         const std::string& profileName) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    CHECK_ARGUMENT_FILE_NAME(profileName);
+    if (!base::EndsWith(profileName, ".prof")) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                         StringPrintf("Profile name %s does not end with .prof",
+                                      profileName.c_str()));
+    }
     LOCK_PACKAGE();
 
     binder::Status res = ok();
@@ -1300,7 +1307,7 @@
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     for (auto userId : get_known_users(uuid_)) {
         LOCK_USER();
-        ATRACE_BEGIN("fixup user");
+        atrace_pm_begin("fixup user");
         FTS* fts;
         FTSENT* p;
         auto ce_path = create_data_user_ce_path(uuid_, userId);
@@ -1390,7 +1397,7 @@
             }
         }
         fts_close(fts);
-        ATRACE_END();
+        atrace_pm_end();
     }
     return ok();
 }
@@ -1928,7 +1935,6 @@
         return error("Failed to determine free space for " + data_path);
     }
 
-    int64_t cleared = 0;
     int64_t needed = targetFreeBytes - free;
     if (!defy_target) {
         LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested "
@@ -1944,7 +1950,7 @@
         // files from the UIDs which are most over their allocated quota
 
         // 1. Create trackers for every known UID
-        ATRACE_BEGIN("create");
+        atrace_pm_begin("create");
         const auto users = get_known_users(uuid_);
 #ifdef GRANULAR_LOCKS
         std::vector<UserLock> userLocks;
@@ -2025,11 +2031,10 @@
             }
             fts_close(fts);
         }
-        ATRACE_END();
+        atrace_pm_end();
 
         // 2. Populate tracker stats and insert into priority queue
-        ATRACE_BEGIN("populate");
-        int64_t cacheTotal = 0;
+        atrace_pm_begin("populate");
         auto cmp = [](std::shared_ptr<CacheTracker> left, std::shared_ptr<CacheTracker> right) {
             return (left->getCacheRatio() < right->getCacheRatio());
         };
@@ -2038,13 +2043,12 @@
         for (const auto& it : trackers) {
             it.second->loadStats();
             queue.push(it.second);
-            cacheTotal += it.second->cacheUsed;
         }
-        ATRACE_END();
+        atrace_pm_end();
 
         // 3. Bounce across the queue, freeing items from whichever tracker is
         // the most over their assigned quota
-        ATRACE_BEGIN("bounce");
+        atrace_pm_begin("bounce");
         std::shared_ptr<CacheTracker> active;
         while (active || !queue.empty()) {
             // Only look at apps under quota when explicitly requested
@@ -2084,7 +2088,6 @@
                 }
                 active->cacheUsed -= item->size;
                 needed -= item->size;
-                cleared += item->size;
             }
 
             if (!defy_target) {
@@ -2101,7 +2104,7 @@
                 }
             }
         }
-        ATRACE_END();
+        atrace_pm_end();
 
     } else {
         return error("Legacy cache logic no longer supported");
@@ -2446,84 +2449,84 @@
         flags &= ~FLAG_USE_QUOTA;
     }
 
-    ATRACE_BEGIN("obb");
+    atrace_pm_begin("obb");
     for (const auto& packageName : packageNames) {
         auto obbCodePath = create_data_media_package_path(uuid_, userId,
                 "obb", packageName.c_str());
         calculate_tree_size(obbCodePath, &extStats.codeSize);
     }
-    ATRACE_END();
+    atrace_pm_end();
     // Calculating the app size of the external storage owning app in a manual way, since
     // calculating it through quota apis also includes external media storage in the app storage
     // numbers
     if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START && !ownsExternalStorage(appId)) {
-        ATRACE_BEGIN("code");
+        atrace_pm_begin("code");
         for (const auto& codePath : codePaths) {
             calculate_tree_size(codePath, &stats.codeSize, -1,
                     multiuser_get_shared_gid(0, appId));
         }
-        ATRACE_END();
+        atrace_pm_end();
 
-        ATRACE_BEGIN("quota");
+        atrace_pm_begin("quota");
         collectQuotaStats(uuidString, userId, appId, &stats, &extStats);
-        ATRACE_END();
+        atrace_pm_end();
     } else {
-        ATRACE_BEGIN("code");
+        atrace_pm_begin("code");
         for (const auto& codePath : codePaths) {
             calculate_tree_size(codePath, &stats.codeSize);
         }
-        ATRACE_END();
+        atrace_pm_end();
 
         for (size_t i = 0; i < packageNames.size(); i++) {
             const char* pkgname = packageNames[i].c_str();
 
-            ATRACE_BEGIN("data");
+            atrace_pm_begin("data");
             auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]);
             collectManualStats(cePath, &stats);
             auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
             collectManualStats(dePath, &stats);
-            ATRACE_END();
+            atrace_pm_end();
 
             // In case of sdk sandbox storage (e.g. /data/misc_ce/0/sdksandbox/<package-name>),
             // collect individual stats of each subdirectory (shared, storage of each sdk etc.)
             if (appId >= AID_APP_START && appId <= AID_APP_END) {
-                ATRACE_BEGIN("sdksandbox");
+                atrace_pm_begin("sdksandbox");
                 auto sdkSandboxCePath =
                         create_data_misc_sdk_sandbox_package_path(uuid_, true, userId, pkgname);
                 collectManualStatsForSubDirectories(sdkSandboxCePath, &stats);
                 auto sdkSandboxDePath =
                         create_data_misc_sdk_sandbox_package_path(uuid_, false, userId, pkgname);
                 collectManualStatsForSubDirectories(sdkSandboxDePath, &stats);
-                ATRACE_END();
+                atrace_pm_end();
             }
 
             if (!uuid) {
-                ATRACE_BEGIN("profiles");
+                atrace_pm_begin("profiles");
                 calculate_tree_size(
                         create_primary_current_profile_package_dir_path(userId, pkgname),
                         &stats.dataSize);
                 calculate_tree_size(
                         create_primary_reference_profile_package_dir_path(pkgname),
                         &stats.codeSize);
-                ATRACE_END();
+                atrace_pm_end();
             }
 
-            ATRACE_BEGIN("external");
+            atrace_pm_begin("external");
             auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname);
             collectManualStats(extPath, &extStats);
             auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname);
             calculate_tree_size(mediaPath, &extStats.dataSize);
-            ATRACE_END();
+            atrace_pm_end();
         }
 
         if (!uuid) {
-            ATRACE_BEGIN("dalvik");
+            atrace_pm_begin("dalvik");
             int32_t sharedGid = multiuser_get_shared_gid(0, appId);
             if (sharedGid != -1) {
                 calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
                         sharedGid, -1);
             }
-            ATRACE_END();
+            atrace_pm_end();
         }
     }
 
@@ -2669,41 +2672,41 @@
     }
 
     if (flags & FLAG_USE_QUOTA) {
-        ATRACE_BEGIN("code");
+        atrace_pm_begin("code");
         calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true);
-        ATRACE_END();
+        atrace_pm_end();
 
-        ATRACE_BEGIN("data");
+        atrace_pm_begin("data");
         auto cePath = create_data_user_ce_path(uuid_, userId);
         collectManualStatsForUser(cePath, &stats, true);
         auto dePath = create_data_user_de_path(uuid_, userId);
         collectManualStatsForUser(dePath, &stats, true);
-        ATRACE_END();
+        atrace_pm_end();
 
         if (!uuid) {
-            ATRACE_BEGIN("profile");
+            atrace_pm_begin("profile");
             auto userProfilePath = create_primary_cur_profile_dir_path(userId);
             calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true);
             auto refProfilePath = create_primary_ref_profile_dir_path();
             calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true);
-            ATRACE_END();
+            atrace_pm_end();
         }
 
-        ATRACE_BEGIN("external");
+        atrace_pm_begin("external");
         auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds);
         extStats.dataSize += sizes.totalSize;
         extStats.codeSize += sizes.obbSize;
-        ATRACE_END();
+        atrace_pm_end();
 
         if (!uuid) {
-            ATRACE_BEGIN("dalvik");
+            atrace_pm_begin("dalvik");
             calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
                     -1, -1, true);
             calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize,
                     -1, -1, true);
-            ATRACE_END();
+            atrace_pm_end();
         }
-        ATRACE_BEGIN("quota");
+        atrace_pm_begin("quota");
         int64_t dataSize = extStats.dataSize;
         for (auto appId : appIds) {
             if (appId >= AID_APP_START) {
@@ -2715,54 +2718,54 @@
             }
         }
         extStats.dataSize = dataSize;
-        ATRACE_END();
+        atrace_pm_end();
     } else {
-        ATRACE_BEGIN("obb");
+        atrace_pm_begin("obb");
         auto obbPath = create_data_path(uuid_) + "/media/obb";
         calculate_tree_size(obbPath, &extStats.codeSize);
-        ATRACE_END();
+        atrace_pm_end();
 
-        ATRACE_BEGIN("code");
+        atrace_pm_begin("code");
         calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize);
-        ATRACE_END();
+        atrace_pm_end();
 
-        ATRACE_BEGIN("data");
+        atrace_pm_begin("data");
         auto cePath = create_data_user_ce_path(uuid_, userId);
         collectManualStatsForUser(cePath, &stats);
         auto dePath = create_data_user_de_path(uuid_, userId);
         collectManualStatsForUser(dePath, &stats);
-        ATRACE_END();
+        atrace_pm_end();
 
-        ATRACE_BEGIN("sdksandbox");
+        atrace_pm_begin("sdksandbox");
         auto sdkSandboxCePath = create_data_misc_sdk_sandbox_path(uuid_, true, userId);
         collectManualStatsForUser(sdkSandboxCePath, &stats, false, true);
         auto sdkSandboxDePath = create_data_misc_sdk_sandbox_path(uuid_, false, userId);
         collectManualStatsForUser(sdkSandboxDePath, &stats, false, true);
-        ATRACE_END();
+        atrace_pm_end();
 
         if (!uuid) {
-            ATRACE_BEGIN("profile");
+            atrace_pm_begin("profile");
             auto userProfilePath = create_primary_cur_profile_dir_path(userId);
             calculate_tree_size(userProfilePath, &stats.dataSize);
             auto refProfilePath = create_primary_ref_profile_dir_path();
             calculate_tree_size(refProfilePath, &stats.codeSize);
-            ATRACE_END();
+            atrace_pm_end();
         }
 
-        ATRACE_BEGIN("external");
+        atrace_pm_begin("external");
         auto dataMediaPath = create_data_media_path(uuid_, userId);
         collectManualExternalStatsForUser(dataMediaPath, &extStats);
 #if MEASURE_DEBUG
         LOG(DEBUG) << "Measured external data " << extStats.dataSize << " cache "
                 << extStats.cacheSize;
 #endif
-        ATRACE_END();
+        atrace_pm_end();
 
         if (!uuid) {
-            ATRACE_BEGIN("dalvik");
+            atrace_pm_begin("dalvik");
             calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize);
             calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize);
-            ATRACE_END();
+            atrace_pm_end();
         }
     }
 
@@ -2810,16 +2813,16 @@
     }
 
     if (flags & FLAG_USE_QUOTA) {
-        ATRACE_BEGIN("quota");
+        atrace_pm_begin("quota");
         auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds);
         totalSize = sizes.totalSize;
         audioSize = sizes.audioSize;
         videoSize = sizes.videoSize;
         imageSize = sizes.imageSize;
         obbSize = sizes.obbSize;
-        ATRACE_END();
+        atrace_pm_end();
 
-        ATRACE_BEGIN("apps");
+        atrace_pm_begin("apps");
         struct stats extStats;
         memset(&extStats, 0, sizeof(extStats));
         for (auto appId : appIds) {
@@ -2828,9 +2831,9 @@
             }
         }
         appSize = extStats.dataSize;
-        ATRACE_END();
+        atrace_pm_end();
     } else {
-        ATRACE_BEGIN("manual");
+        atrace_pm_begin("manual");
         FTS *fts;
         FTSENT *p;
         auto path = create_data_media_path(uuid_, userId);
@@ -2873,16 +2876,16 @@
             }
         }
         fts_close(fts);
-        ATRACE_END();
+        atrace_pm_end();
 
-        ATRACE_BEGIN("obb");
+        atrace_pm_begin("obb");
         auto obbPath = StringPrintf("%s/Android/obb",
                 create_data_media_path(uuid_, userId).c_str());
         calculate_tree_size(obbPath, &obbSize);
         if (!(flags & FLAG_USE_QUOTA)) {
             totalSize -= obbSize;
         }
-        ATRACE_END();
+        atrace_pm_end();
     }
 
     std::vector<int64_t> ret;
@@ -3025,7 +3028,19 @@
         int32_t packageUid, const std::string& packageName, const std::string& profileName,
         bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PATH(systemProfile);
+    if (!base::EndsWith(systemProfile, ".prof")) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                         StringPrintf("System profile path %s does not end with .prof",
+                                      systemProfile.c_str()));
+    }
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    CHECK_ARGUMENT_FILE_NAME(profileName);
+    if (!base::EndsWith(profileName, ".prof")) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                         StringPrintf("Profile name %s does not end with .prof",
+                                      profileName.c_str()));
+    }
     LOCK_PACKAGE();
     *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName);
     return ok();
@@ -3545,16 +3560,28 @@
 
     std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
     if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
-        return error("Failed to create CE mirror");
+        return error("Failed to create CE data mirror");
     }
 
     std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
     if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
-        return error("Failed to create DE mirror");
+        return error("Failed to create DE data mirror");
+    }
+
+    std::string mirrorVolMiscCePath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+    if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create CE misc mirror");
+    }
+
+    std::string mirrorVolMiscDePath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
+    if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create DE misc mirror");
     }
 
     auto cePath = StringPrintf("%s/user", create_data_path(uuid_).c_str());
     auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+    auto miscCePath = StringPrintf("%s/misc_ce", create_data_path(uuid_).c_str());
+    auto miscDePath = StringPrintf("%s/misc_de", create_data_path(uuid_).c_str());
 
     if (access(cePath.c_str(), F_OK) != 0) {
         return error("Cannot access CE path: " + cePath);
@@ -3562,6 +3589,12 @@
     if (access(dePath.c_str(), F_OK) != 0) {
         return error("Cannot access DE path: " + dePath);
     }
+    if (access(miscCePath.c_str(), F_OK) != 0) {
+        return error("Cannot access misc CE path: " + cePath);
+    }
+    if (access(miscDePath.c_str(), F_OK) != 0) {
+        return error("Cannot access misc DE path: " + dePath);
+    }
 
     struct stat ceStat, mirrorCeStat;
     if (stat(cePath.c_str(), &ceStat) != 0) {
@@ -3589,6 +3622,21 @@
             MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
         return error("Failed to mount " + mirrorVolDePath);
     }
+
+    // Mount misc CE mirror
+    if (TEMP_FAILURE_RETRY(mount(miscCePath.c_str(), mirrorVolMiscCePath.c_str(), NULL,
+                                 MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+                                 nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolMiscCePath);
+    }
+
+    // Mount misc DE mirror
+    if (TEMP_FAILURE_RETRY(mount(miscDePath.c_str(), mirrorVolMiscDePath.c_str(), NULL,
+                                 MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+                                 nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolMiscDePath);
+    }
+
     return ok();
 }
 
@@ -3611,6 +3659,8 @@
 
     std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
     std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+    std::string mirrorMiscCeVolPath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+    std::string mirrorMiscDeVolPath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
 
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
 
@@ -3635,6 +3685,29 @@
     if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
         res = error("Failed to delete " + mirrorDeVolPath);
     }
+
+    // Unmount misc CE storage
+    if (TEMP_FAILURE_RETRY(umount(mirrorMiscCeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorMiscCeVolPath.c_str(),
+                                     strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorMiscCeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorMiscCeVolPath);
+    }
+
+    // Unmount misc DE storage
+    if (TEMP_FAILURE_RETRY(umount(mirrorMiscDeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorMiscDeVolPath.c_str(),
+                                     strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorMiscDeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorMiscDeVolPath);
+    }
+
     return res;
 }
 
@@ -3674,7 +3747,7 @@
     ENFORCE_UID(AID_SYSTEM);
     // NOTE: The lint warning doesn't apply to the use of system(3) with
     // absolute parse and no command line arguments.
-    if (system("/system/bin/migrate_legacy_obb_data.sh") != 0) { // NOLINT(cert-env33-c)
+    if (system("/system/bin/migrate_legacy_obb_data") != 0) { // NOLINT(cert-env33-c)
         LOG(ERROR) << "Unable to migrate legacy obb data";
     }
 
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/cmds/installd/SysTrace.cpp
similarity index 63%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to cmds/installd/SysTrace.cpp
index 9fac3e8..fa65c77 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/cmds/installd/SysTrace.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,17 @@
  * limitations under the License.
  */
 
-package android.gui;
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
+#include "SysTrace.h"
+#include <utils/Trace.h>
+
+namespace android::installd {
+void atrace_pm_begin(const char* name) {
+    ATRACE_BEGIN(name);
 }
+
+void atrace_pm_end() {
+    ATRACE_END();
+}
+} /* namespace android::installd */
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/cmds/installd/SysTrace.h
similarity index 74%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to cmds/installd/SysTrace.h
index 9fac3e8..18506a9 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/cmds/installd/SysTrace.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package android.gui;
+#pragma once
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
-}
+namespace android::installd {
+void atrace_pm_begin(const char*);
+void atrace_pm_end();
+} /* namespace android::installd */
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index e978e79..6a3120c 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -27,6 +27,7 @@
 #include <sys/prctl.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
+#include <sys/wait.h>
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -504,6 +505,11 @@
             return 0;
         }
 
+        if (WIFSIGNALED(dexopt_result)) {
+            LOG(WARNING) << "Interrupted by signal " << WTERMSIG(dexopt_result) ;
+            return dexopt_result;
+        }
+
         // If this was a profile-guided run, we may have profile version issues. Try to downgrade,
         // if possible.
         if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
diff --git a/cmds/installd/otapreopt.rc b/cmds/installd/otapreopt.rc
index 059ae75..0bad0c5 100644
--- a/cmds/installd/otapreopt.rc
+++ b/cmds/installd/otapreopt.rc
@@ -5,4 +5,4 @@
     # The dalvik-cache was not moved itself, so as to restrict the rights of otapreopt_slot.
     # But now the relabeling is annoying as there is no force option available here. So
     # explicitly list all the ISAs we know.
-    restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/mips /data/dalvik-cache/mips64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64
+    restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/riscv64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 3849c40..6ef41e3 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -1371,6 +1371,58 @@
                           /*has_user_id*/ true, /*expected_result*/ false);
 }
 
+TEST_F(ProfileTest, ClearAppProfilesOk) {
+    LOG(INFO) << "ClearAppProfilesOk";
+
+    ASSERT_BINDER_SUCCESS(service_->clearAppProfiles(package_name_, "primary.prof"));
+    ASSERT_BINDER_SUCCESS(service_->clearAppProfiles(package_name_, "image_editor.split.prof"));
+}
+
+TEST_F(ProfileTest, ClearAppProfilesFailWrongProfileName) {
+    LOG(INFO) << "ClearAppProfilesFailWrongProfileName";
+
+    ASSERT_BINDER_FAIL(
+            service_->clearAppProfiles(package_name_,
+                                       "../../../../dalvik-cache/arm64/"
+                                       "system@app@SecureElement@SecureElement.apk@classes.vdex"));
+    ASSERT_BINDER_FAIL(service_->clearAppProfiles(package_name_, "image_editor.split.apk"));
+}
+
+TEST_F(ProfileTest, CopySystemProfileOk) {
+    LOG(INFO) << "CopySystemProfileOk";
+
+    bool result;
+    ASSERT_BINDER_SUCCESS(
+            service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof",
+                                        kTestAppUid, package_name_, "primary.prof", &result));
+}
+
+TEST_F(ProfileTest, CopySystemProfileFailWrongSystemProfilePath) {
+    LOG(INFO) << "CopySystemProfileFailWrongSystemProfilePath";
+
+    bool result;
+    ASSERT_BINDER_FAIL(service_->copySystemProfile("../../secret.dat", kTestAppUid, package_name_,
+                                                   "primary.prof", &result));
+    ASSERT_BINDER_FAIL(service_->copySystemProfile("/data/user/package.name/secret.data",
+                                                   kTestAppUid, package_name_, "primary.prof",
+                                                   &result));
+}
+
+TEST_F(ProfileTest, CopySystemProfileFailWrongProfileName) {
+    LOG(INFO) << "CopySystemProfileFailWrongProfileName";
+
+    bool result;
+    ASSERT_BINDER_FAIL(
+            service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof",
+                                        kTestAppUid, package_name_,
+                                        "../../../../dalvik-cache/arm64/test.vdex", &result));
+    ASSERT_BINDER_FAIL(
+            service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof",
+                                        kTestAppUid, package_name_, "/test.prof", &result));
+    ASSERT_BINDER_FAIL(
+            service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof",
+                                        kTestAppUid, package_name_, "base.apk", &result));
+}
 
 class BootProfileTest : public ProfileTest {
   public:
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 4d9b710..ffc082d 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -523,7 +523,6 @@
  */
 bool is_valid_package_name(const std::string& packageName) {
     // This logic is borrowed from PackageParser.java
-    bool hasSep = false;
     bool front = true;
 
     auto it = packageName.begin();
@@ -539,7 +538,6 @@
             }
         }
         if (c == '.') {
-            hasSep = true;
             front = true;
             continue;
         }
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 25bd9a3..fd879c6 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -90,29 +90,16 @@
 
 cc_fuzz {
     name: "servicemanager_fuzzer",
-    defaults: ["servicemanager_defaults"],
-    host_supported: true,
-    static_libs: [
-        "libbase",
-        "libbinder_random_parcel",
-        "libcutils",
+    defaults: [
+        "servicemanager_defaults",
+        "service_fuzzer_defaults",
     ],
-    target: {
-        android: {
-            shared_libs: [
-                "libbinder_ndk",
-                "libbinder",
-            ],
-        },
-        host: {
-            static_libs: [
-                "libbinder_ndk",
-                "libbinder",
-            ],
-        },
-    },
+    host_supported: true,
     srcs: ["ServiceManagerFuzzer.cpp"],
     fuzz_config: {
+        libfuzzer_options: [
+            "max_len=50000",
+        ],
         cc: [
             "smoreland@google.com",
             "waghpawan@google.com",
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 3cfe529..3681d5b 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -49,14 +49,14 @@
 #ifdef __ANDROID_RECOVERY__
     auto vintfObject = vintf::VintfObjectRecovery::GetInstance();
     if (vintfObject == nullptr) {
-        LOG(ERROR) << "NULL VintfObjectRecovery!";
+        ALOGE("NULL VintfObjectRecovery!");
         return {};
     }
     return {ManifestWithDescription{vintfObject->getRecoveryHalManifest(), "recovery"}};
 #else
     auto vintfObject = vintf::VintfObject::GetInstance();
     if (vintfObject == nullptr) {
-        LOG(ERROR) << "NULL VintfObject!";
+        ALOGE("NULL VintfObject!");
         return {};
     }
     return {ManifestWithDescription{vintfObject->getDeviceHalManifest(), "device"},
@@ -68,10 +68,10 @@
 static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
     for (const ManifestWithDescription& mwd : GetManifestsWithDescription()) {
         if (mwd.manifest == nullptr) {
-          LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
-          // note, we explicitly do not retry here, so that we can detect VINTF
-          // or other bugs (b/151696835)
-          continue;
+            ALOGE("NULL VINTF MANIFEST!: %s", mwd.description);
+            // note, we explicitly do not retry here, so that we can detect VINTF
+            // or other bugs (b/151696835)
+            continue;
         }
         if (func(mwd)) return true;
     }
@@ -87,8 +87,9 @@
         size_t firstSlash = name.find('/');
         size_t lastDot = name.rfind('.', firstSlash);
         if (firstSlash == std::string::npos || lastDot == std::string::npos) {
-            LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. "
-                       << "some.package.foo.IFoo/default) but got: " << name;
+            ALOGE("VINTF HALs require names in the format type/instance (e.g. "
+                  "some.package.foo.IFoo/default) but got: %s",
+                  name.c_str());
             return false;
         }
         aname->package = name.substr(0, lastDot);
@@ -104,7 +105,7 @@
 
     bool found = forEachManifest([&](const ManifestWithDescription& mwd) {
         if (mwd.manifest->hasAidlInstance(aname.package, aname.iface, aname.instance)) {
-            LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
+            ALOGI("Found %s in %s VINTF manifest.", name.c_str(), mwd.description);
             return true; // break
         }
         return false;  // continue
@@ -113,8 +114,8 @@
     if (!found) {
         // Although it is tested, explicitly rebuilding qualified name, in case it
         // becomes something unexpected.
-        LOG(INFO) << "Could not find " << aname.package << "." << aname.iface << "/"
-                  << aname.instance << " in the VINTF manifest.";
+        ALOGI("Could not find %s.%s/%s in the VINTF manifest.", aname.package.c_str(),
+              aname.iface.c_str(), aname.instance.c_str());
     }
 
     return found;
@@ -173,7 +174,9 @@
 static std::vector<std::string> getVintfInstances(const std::string& interface) {
     size_t lastDot = interface.rfind('.');
     if (lastDot == std::string::npos) {
-        LOG(ERROR) << "VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) but got: " << interface;
+        ALOGE("VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) "
+              "but got: %s",
+              interface.c_str());
         return {};
     }
     const std::string package = interface.substr(0, lastDot);
@@ -308,7 +311,7 @@
     }
 
     if (!isValidServiceName(name)) {
-        LOG(ERROR) << "Invalid service name: " << name;
+        ALOGE("Invalid service name: %s", name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name");
     }
 
@@ -322,20 +325,44 @@
     // implicitly unlinked when the binder is removed
     if (binder->remoteBinder() != nullptr &&
         binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
-        LOG(ERROR) << "Could not linkToDeath when adding " << name;
+        ALOGE("Could not linkToDeath when adding %s", name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure");
     }
 
+    auto it = mNameToService.find(name);
+    if (it != mNameToService.end()) {
+        const Service& existing = it->second;
+
+        // We could do better than this because if the other service dies, it
+        // may not have an entry here. However, this case is unlikely. We are
+        // only trying to detect when two different services are accidentally installed.
+
+        if (existing.ctx.uid != ctx.uid) {
+            ALOGW("Service '%s' originally registered from UID %u but it is now being registered "
+                  "from UID %u. Multiple instances installed?",
+                  name.c_str(), existing.ctx.uid, ctx.uid);
+        }
+
+        if (existing.ctx.sid != ctx.sid) {
+            ALOGW("Service '%s' originally registered from SID %s but it is now being registered "
+                  "from SID %s. Multiple instances installed?",
+                  name.c_str(), existing.ctx.sid.c_str(), ctx.sid.c_str());
+        }
+
+        ALOGI("Service '%s' originally registered from PID %d but it is being registered again "
+              "from PID %d. Bad state? Late death notification? Multiple instances installed?",
+              name.c_str(), existing.ctx.debugPid, ctx.debugPid);
+    }
+
     // Overwrite the old service if it exists
-    mNameToService[name] = Service {
-        .binder = binder,
-        .allowIsolated = allowIsolated,
-        .dumpPriority = dumpPriority,
-        .debugPid = ctx.debugPid,
+    mNameToService[name] = Service{
+            .binder = binder,
+            .allowIsolated = allowIsolated,
+            .dumpPriority = dumpPriority,
+            .ctx = ctx,
     };
 
-    auto it = mNameToRegistrationCallback.find(name);
-    if (it != mNameToRegistrationCallback.end()) {
+    if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) {
         for (const sp<IServiceCallback>& cb : it->second) {
             mNameToService[name].guaranteeClient = true;
             // permission checked in registerForNotifications
@@ -381,7 +408,7 @@
     }
 
     if (!isValidServiceName(name)) {
-        LOG(ERROR) << "Invalid service name: " << name;
+        ALOGE("Invalid service name: %s", name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
@@ -392,7 +419,7 @@
     if (OK !=
         IInterface::asBinder(callback)->linkToDeath(
                 sp<ServiceManager>::fromExisting(this))) {
-        LOG(ERROR) << "Could not linkToDeath when adding " << name;
+        ALOGE("Could not linkToDeath when adding %s", name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
@@ -424,7 +451,7 @@
     }
 
     if (!found) {
-        LOG(ERROR) << "Trying to unregister callback, but none exists " << name;
+        ALOGE("Trying to unregister callback, but none exists %s", name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
@@ -546,10 +573,11 @@
 
     std::thread([=] {
         if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
-            LOG(INFO) << "Tried to start aidl service " << name
-                      << " as a lazy service, but was unable to. Usually this happens when a "
-                         "service is not installed, but if the service is intended to be used as a "
-                         "lazy service, then it may be configured incorrectly.";
+            ALOGI("Tried to start aidl service %s as a lazy service, but was unable to. Usually "
+                  "this happens when a "
+                  "service is not installed, but if the service is intended to be used as a "
+                  "lazy service, then it may be configured incorrectly.",
+                  name.c_str());
         }
     }).detach();
 }
@@ -567,24 +595,25 @@
 
     auto serviceIt = mNameToService.find(name);
     if (serviceIt == mNameToService.end()) {
-        LOG(ERROR) << "Could not add callback for nonexistent service: " << name;
+        ALOGE("Could not add callback for nonexistent service: %s", name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
-    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
-        LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
+    if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
+        ALOGW("Only a server can register for client callbacks (for %s)", name.c_str());
         return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
     }
 
     if (serviceIt->second.binder != service) {
-        LOG(WARNING) << "Tried to register client callback for " << name
-            << " but a different service is registered under this name.";
+        ALOGW("Tried to register client callback for %s but a different service is registered "
+              "under this name.",
+              name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
     if (OK !=
         IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
-        LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
+        ALOGE("Could not linkToDeath when adding client callback for %s", name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
@@ -669,7 +698,7 @@
 void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
     auto serviceIt = mNameToService.find(serviceName);
     if (serviceIt == mNameToService.end()) {
-        LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName;
+        ALOGW("sendClientCallbackNotifications could not find service %s", serviceName.c_str());
         return;
     }
     Service& service = serviceIt->second;
@@ -677,7 +706,7 @@
     CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
         << " so we can't tell clients again that we have client: " << hasClients;
 
-    LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients;
+    ALOGI("Notifying %s they have clients: %d", serviceName.c_str(), hasClients);
 
     auto ccIt = mNameToClientCallback.find(serviceName);
     CHECK(ccIt != mNameToClientCallback.end())
@@ -702,26 +731,26 @@
 
     auto serviceIt = mNameToService.find(name);
     if (serviceIt == mNameToService.end()) {
-        LOG(WARNING) << "Tried to unregister " << name
-            << ", but that service wasn't registered to begin with.";
+        ALOGW("Tried to unregister %s, but that service wasn't registered to begin with.",
+              name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
-    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
-        LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
+    if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
+        ALOGW("Only a server can unregister itself (for %s)", name.c_str());
         return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
     }
 
     sp<IBinder> storedBinder = serviceIt->second.binder;
 
     if (binder != storedBinder) {
-        LOG(WARNING) << "Tried to unregister " << name
-            << ", but a different service is registered under this name.";
+        ALOGW("Tried to unregister %s, but a different service is registered under this name.",
+              name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
     if (serviceIt->second.guaranteeClient) {
-        LOG(INFO) << "Tried to unregister " << name << ", but there is about to be a client.";
+        ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str());
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
@@ -734,7 +763,7 @@
     // So, if clients > 2, then at least one other service on the system must hold a refcount.
     if (clients < 0 || clients > 2) {
         // client callbacks are either disabled or there are other clients
-        LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients;
+        ALOGI("Tried to unregister %s, but there are clients: %d", name.c_str(), clients);
         // Set this flag to ensure the clients are acknowledged in the next callback
         serviceIt->second.guaranteeClient = true;
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
@@ -754,7 +783,7 @@
     for (auto const& [name, service] : mNameToService) {
         ServiceDebugInfo info;
         info.name = name;
-        info.debugPid = service.debugPid;
+        info.debugPid = service.ctx.debugPid;
 
         outReturn->push_back(std::move(info));
     }
@@ -762,4 +791,10 @@
     return Status::ok();
 }
 
+void ServiceManager::clear() {
+    mNameToService.clear();
+    mNameToRegistrationCallback.clear();
+    mNameToClientCallback.clear();
+}
+
 }  // namespace android
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 5e40319..07b79f8 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -58,6 +58,12 @@
     void binderDied(const wp<IBinder>& who) override;
     void handleClientCallbacks();
 
+    /**
+     *  This API is added for debug purposes. It clears members which hold service and callback
+     * information.
+     */
+    void clear();
+
 protected:
     virtual void tryStartService(const std::string& name);
 
@@ -68,7 +74,7 @@
         int32_t dumpPriority;
         bool hasClients = false; // notifications sent on true -> false.
         bool guaranteeClient = false; // forces the client check to true
-        pid_t debugPid = 0; // the process in which this service runs
+        Access::CallingContext ctx;   // process that originally registers this
 
         // the number of clients of the service, including servicemanager itself
         ssize_t getNodeStrongRefCount();
diff --git a/cmds/servicemanager/ServiceManagerFuzzer.cpp b/cmds/servicemanager/ServiceManagerFuzzer.cpp
index 9e2e53f..b76a6bd 100644
--- a/cmds/servicemanager/ServiceManagerFuzzer.cpp
+++ b/cmds/servicemanager/ServiceManagerFuzzer.cpp
@@ -26,13 +26,10 @@
 using ::android::sp;
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    if (size > 50000) {
-        return 0;
-    }
-
     auto accessPtr = std::make_unique<Access>();
     auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr));
     fuzzService(serviceManager, FuzzedDataProvider(data, size));
+    serviceManager->clear();
 
     return 0;
-}
\ No newline at end of file
+}
diff --git a/cmds/surfacereplayer/Android.bp b/cmds/surfacereplayer/Android.bp
deleted file mode 100644
index 34fc8b1..0000000
--- a/cmds/surfacereplayer/Android.bp
+++ /dev/null
@@ -1,13 +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"],
-}
-
-subdirs = [
-    "proto",
-    "replayer",
-]
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS
deleted file mode 100644
index 32bcc83..0000000
--- a/cmds/surfacereplayer/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp
deleted file mode 100644
index 23b54ee..0000000
--- a/cmds/surfacereplayer/proto/Android.bp
+++ /dev/null
@@ -1,23 +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"],
-}
-
-cc_library_static {
-    name: "libtrace_proto",
-    srcs: [
-        "src/trace.proto",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    proto: {
-        type: "lite",
-        export_proto_headers: true,
-    },
-}
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
deleted file mode 100644
index a177027..0000000
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ /dev/null
@@ -1,225 +0,0 @@
-syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
-package android.surfaceflinger;
-
-message Trace {
-    repeated Increment increment = 1;
-}
-
-message Increment {
-    required int64 time_stamp = 1;
-
-    oneof increment {
-        Transaction        transaction          = 2;
-        SurfaceCreation    surface_creation     = 3;
-        SurfaceDeletion    surface_deletion     = 4;
-        BufferUpdate       buffer_update        = 5;
-        VSyncEvent         vsync_event          = 6;
-        DisplayCreation    display_creation     = 7;
-        DisplayDeletion    display_deletion     = 8;
-        PowerModeUpdate    power_mode_update    = 9;
-    }
-}
-
-message Transaction {
-    repeated SurfaceChange surface_change = 1;
-    repeated DisplayChange display_change = 2;
-
-    required bool   synchronous      = 3;
-    required bool   animation        = 4;
-    optional Origin origin           = 5;
-    optional uint64 id               = 6;
-}
-
-message SurfaceChange {
-    required int32 id = 1;
-    reserved 7;
-    oneof SurfaceChange {
-        PositionChange              position                = 2;
-        SizeChange                  size                    = 3;
-        AlphaChange                 alpha                   = 4;
-        LayerChange                 layer                   = 5;
-        CropChange                  crop                    = 6;
-        MatrixChange                matrix                  = 8;
-        TransparentRegionHintChange transparent_region_hint = 10;
-        LayerStackChange            layer_stack             = 11;
-        HiddenFlagChange            hidden_flag             = 12;
-        OpaqueFlagChange            opaque_flag             = 13;
-        SecureFlagChange            secure_flag             = 14;
-        CornerRadiusChange          corner_radius           = 16;
-        ReparentChange              reparent                = 17;
-        RelativeParentChange        relative_parent         = 18;
-        BackgroundBlurRadiusChange  background_blur_radius  = 20;
-        ShadowRadiusChange          shadow_radius           = 21;
-        BlurRegionsChange           blur_regions            = 22;
-        TrustedOverlayChange        trusted_overlay         = 23;
-    }
-}
-
-message PositionChange {
-    required float x = 1;
-    required float y = 2;
-}
-
-message SizeChange {
-    required uint32 w = 1;
-    required uint32 h = 2;
-}
-
-message AlphaChange {
-    required float alpha = 1;
-}
-
-message CornerRadiusChange {
-    required float corner_radius = 1;
-}
-
-message BackgroundBlurRadiusChange {
-    required float background_blur_radius = 1;
-}
-
-message LayerChange {
-    required uint32 layer = 1;
-}
-
-message CropChange {
-    required Rectangle rectangle = 1;
-}
-
-message MatrixChange {
-    required float dsdx = 1;
-    required float dtdx = 2;
-    required float dsdy = 3;
-    required float dtdy = 4;
-}
-
-message TransparentRegionHintChange {
-    repeated Rectangle region = 1;
-}
-
-message LayerStackChange {
-    required uint32 layer_stack = 1;
-}
-
-message DisplayFlagsChange {
-    required uint32 flags = 1;
-}
-
-message HiddenFlagChange {
-    required bool hidden_flag = 1;
-}
-
-message OpaqueFlagChange {
-    required bool opaque_flag = 1;
-}
-
-message SecureFlagChange {
-    required bool secure_flag = 1;
-}
-
-message DisplayChange {
-    required int32 id = 1;
-
-    oneof DisplayChange {
-        DispSurfaceChange surface     = 2;
-        LayerStackChange  layer_stack = 3;
-        SizeChange        size        = 4;
-        ProjectionChange  projection  = 5;
-        DisplayFlagsChange flags      = 6;
-    }
-}
-
-message DispSurfaceChange {
-    required uint64 buffer_queue_id   = 1;
-    required string buffer_queue_name = 2;
-}
-
-message ProjectionChange {
-    required int32     orientation = 1;
-    required Rectangle viewport    = 2;
-    required Rectangle frame       = 3;
-}
-
-message Rectangle {
-    required int32 left   = 1;
-    required int32 top    = 2;
-    required int32 right  = 3;
-    required int32 bottom = 4;
-}
-
-message SurfaceCreation {
-    required int32  id   = 1;
-    required string name = 2;
-    required uint32 w    = 3;
-    required uint32 h    = 4;
-}
-
-message SurfaceDeletion {
-    required int32 id = 1;
-}
-
-message BufferUpdate {
-    required int32  id           = 1;
-    required uint32 w            = 2;
-    required uint32 h            = 3;
-    required uint64 frame_number = 4;
-}
-
-message VSyncEvent {
-    required int64 when = 1;
-}
-
-message DisplayCreation {
-    required int32     id                = 1;
-    required string    name              = 2;
-    optional uint64    display_id        = 3;
-    required bool      is_secure         = 4;
-}
-
-message DisplayDeletion {
-    required int32 id = 1;
-}
-
-message PowerModeUpdate {
-    required int32  id   = 1;
-    required int32  mode = 2;
-}
-
-message ReparentChange {
-    required int32 parent_id = 1;
-}
-
-message RelativeParentChange {
-    required int32 relative_parent_id = 1;
-    required int32 z = 2;
-}
-
-message ShadowRadiusChange {
-    required float radius = 1;
-}
-
-message TrustedOverlayChange {
-    required float is_trusted_overlay = 1;
-}
-
-message BlurRegionsChange {
-    repeated BlurRegionChange blur_regions = 1;
-}
-
-message BlurRegionChange {
-    required uint32 blur_radius = 1;
-    required float corner_radius_tl = 2;
-    required float corner_radius_tr = 3;
-    required float corner_radius_bl = 4;
-    required float corner_radius_br = 5;
-    required float alpha = 6;
-    required int32 left = 7;
-    required int32 top = 8;
-    required int32 right = 9;
-    required int32 bottom = 10;
-}
-
-message Origin {
-    required int32 pid = 1;
-    required int32 uid = 2;
-}
diff --git a/cmds/surfacereplayer/replayer/Android.bp b/cmds/surfacereplayer/replayer/Android.bp
deleted file mode 100644
index 3985230..0000000
--- a/cmds/surfacereplayer/replayer/Android.bp
+++ /dev/null
@@ -1,71 +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"],
-}
-
-cc_library_shared {
-    name: "libsurfacereplayer",
-    srcs: [
-        "BufferQueueScheduler.cpp",
-        "Event.cpp",
-        "Replayer.cpp",
-    ],
-    cppflags: [
-        "-Werror",
-        "-Wno-unused-parameter",
-        "-Wno-format",
-	"-Wno-c++98-compat-pedantic",
-	"-Wno-float-conversion",
-	"-Wno-disabled-macro-expansion",
-	"-Wno-float-equal",
-	"-Wno-sign-conversion",
-	"-Wno-padded",
-    ],
-    static_libs: [
-        "libtrace_proto",
-    ],
-    shared_libs: [
-        "libEGL",
-        "libGLESv2",
-        "libbinder",
-        "liblog",
-        "libcutils",
-        "libgui",
-        "libui",
-        "libutils",
-        "libprotobuf-cpp-lite",
-        "libbase",
-        "libnativewindow",
-    ],
-    export_include_dirs: [
-        ".",
-    ],
-}
-
-cc_binary {
-    name: "surfacereplayer",
-    srcs: [
-        "Main.cpp",
-    ],
-    shared_libs: [
-        "libprotobuf-cpp-lite",
-        "libsurfacereplayer",
-        "libutils",
-        "libgui",
-    ],
-    static_libs: [
-        "libtrace_proto",
-    ],
-    cppflags: [
-        "-Werror",
-        "-Wno-unused-parameter",
-	"-Wno-c++98-compat-pedantic",
-	"-Wno-float-conversion",
-	"-Wno-disabled-macro-expansion",
-	"-Wno-float-equal",
-    ],
-}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
deleted file mode 100644
index 77de8dc..0000000
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "BufferQueueScheduler"
-
-#include "BufferQueueScheduler.h"
-
-#include <android/native_window.h>
-#include <gui/Surface.h>
-
-using namespace android;
-
-BufferQueueScheduler::BufferQueueScheduler(
-        const sp<SurfaceControl>& surfaceControl, const HSV& color, int id)
-      : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {}
-
-void BufferQueueScheduler::startScheduling() {
-    ALOGV("Starting Scheduler for %d Layer", mSurfaceId);
-    std::unique_lock<std::mutex> lock(mMutex);
-    if (mSurfaceControl == nullptr) {
-        mCondition.wait(lock, [&] { return (mSurfaceControl != nullptr); });
-    }
-
-    while (mContinueScheduling) {
-        while (true) {
-            if (mBufferEvents.empty()) {
-                break;
-            }
-
-            BufferEvent event = mBufferEvents.front();
-            lock.unlock();
-
-            bufferUpdate(event.dimensions);
-            fillSurface(event.event);
-            mColor.modulate();
-            lock.lock();
-            mBufferEvents.pop();
-        }
-        mCondition.wait(lock);
-    }
-}
-
-void BufferQueueScheduler::addEvent(const BufferEvent& event) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mBufferEvents.push(event);
-    mCondition.notify_one();
-}
-
-void BufferQueueScheduler::stopScheduling() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mContinueScheduling = false;
-    mCondition.notify_one();
-}
-
-void BufferQueueScheduler::setSurfaceControl(
-        const sp<SurfaceControl>& surfaceControl, const HSV& color) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mSurfaceControl = surfaceControl;
-    mColor = color;
-    mCondition.notify_one();
-}
-
-void BufferQueueScheduler::bufferUpdate(const Dimensions& dimensions) {
-    sp<Surface> s = mSurfaceControl->getSurface();
-    s->setBuffersDimensions(dimensions.width, dimensions.height);
-}
-
-void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = mSurfaceControl->getSurface();
-
-    status_t status = s->lock(&outBuffer, nullptr);
-
-    if (status != NO_ERROR) {
-        ALOGE("fillSurface: failed to lock buffer, (%d)", status);
-        return;
-    }
-
-    auto color = mColor.getRGB();
-
-    auto img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
-            pixel[0] = color.r;
-            pixel[1] = color.g;
-            pixel[2] = color.b;
-            pixel[3] = LAYER_ALPHA;
-        }
-    }
-
-    event->readyToExecute();
-
-    status = s->unlockAndPost();
-
-    ALOGE_IF(status != NO_ERROR, "fillSurface: failed to unlock and post buffer, (%d)", status);
-}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
deleted file mode 100644
index cb20fcc..0000000
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
-#define ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
-
-#include "Color.h"
-#include "Event.h"
-
-#include <gui/SurfaceControl.h>
-
-#include <utils/StrongPointer.h>
-
-#include <atomic>
-#include <condition_variable>
-#include <mutex>
-#include <queue>
-#include <utility>
-
-namespace android {
-
-auto constexpr LAYER_ALPHA = 190;
-
-struct Dimensions {
-    Dimensions() = default;
-    Dimensions(int w, int h) : width(w), height(h) {}
-
-    int width = 0;
-    int height = 0;
-};
-
-struct BufferEvent {
-    BufferEvent() = default;
-    BufferEvent(std::shared_ptr<Event> e, Dimensions d) : event(e), dimensions(d) {}
-
-    std::shared_ptr<Event> event;
-    Dimensions dimensions;
-};
-
-class BufferQueueScheduler {
-  public:
-    BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id);
-
-    void startScheduling();
-    void addEvent(const BufferEvent&);
-    void stopScheduling();
-
-    void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color);
-
-  private:
-    void bufferUpdate(const Dimensions& dimensions);
-
-    // Lock and fill the surface, block until the event is signaled by the main loop,
-    // then unlock and post the buffer.
-    void fillSurface(const std::shared_ptr<Event>& event);
-
-    sp<SurfaceControl> mSurfaceControl;
-    HSV mColor;
-    const int mSurfaceId;
-
-    bool mContinueScheduling;
-
-    std::queue<BufferEvent> mBufferEvents;
-    std::mutex mMutex;
-    std::condition_variable mCondition;
-};
-
-}  // namespace android
-#endif
diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h
deleted file mode 100644
index ce644be..0000000
--- a/cmds/surfacereplayer/replayer/Color.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_COLOR_H
-#define ANDROID_SURFACEREPLAYER_COLOR_H
-
-#include <cmath>
-#include <cstdlib>
-
-namespace android {
-
-constexpr double modulateFactor = .0001;
-constexpr double modulateLimit = .80;
-
-struct RGB {
-    RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {}
-
-    uint8_t r = 0;
-    uint8_t g = 0;
-    uint8_t b = 0;
-};
-
-struct HSV {
-    HSV() = default;
-    HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {}
-
-    double h = 0;
-    double s = 0;
-    double v = 0;
-
-    RGB getRGB() const;
-
-    bool modulateUp = false;
-
-    void modulate();
-};
-
-void inline HSV::modulate() {
-    if(modulateUp) {
-        v += modulateFactor;
-    } else {
-        v -= modulateFactor;
-    }
-
-    if(v <= modulateLimit || v >= 1) {
-        modulateUp = !modulateUp;
-    }
-}
-
-inline RGB HSV::getRGB() const {
-    using namespace std;
-    double r = 0, g = 0, b = 0;
-
-    if (s == 0) {
-        r = v;
-        g = v;
-        b = v;
-    } else {
-        auto tempHue = static_cast<int>(h) % 360;
-        tempHue = tempHue / 60;
-
-        int i = static_cast<int>(trunc(tempHue));
-        double f = h - i;
-
-        double x = v * (1.0 - s);
-        double y = v * (1.0 - (s * f));
-        double z = v * (1.0 - (s * (1.0 - f)));
-
-        switch (i) {
-            case 0:
-                r = v;
-                g = z;
-                b = x;
-                break;
-
-            case 1:
-                r = y;
-                g = v;
-                b = x;
-                break;
-
-            case 2:
-                r = x;
-                g = v;
-                b = z;
-                break;
-
-            case 3:
-                r = x;
-                g = y;
-                b = v;
-                break;
-
-            case 4:
-                r = z;
-                g = x;
-                b = v;
-                break;
-
-            default:
-                r = v;
-                g = x;
-                b = y;
-                break;
-        }
-    }
-
-    return RGB(round(r * 255), round(g * 255), round(b * 255));
-}
-}
-#endif
diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp
deleted file mode 100644
index 64db5f0..0000000
--- a/cmds/surfacereplayer/replayer/Event.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-
-#include "Event.h"
-
-using namespace android;
-using Increment = surfaceflinger::Increment;
-
-Event::Event(Increment::IncrementCase type) : mIncrementType(type) {}
-
-void Event::readyToExecute() {
-    changeState(Event::EventState::Waiting);
-    waitUntil(Event::EventState::Signaled);
-    changeState(Event::EventState::Running);
-}
-
-void Event::complete() {
-    waitUntil(Event::EventState::Waiting);
-    changeState(Event::EventState::Signaled);
-    waitUntil(Event::EventState::Running);
-}
-
-void Event::waitUntil(Event::EventState state) {
-    std::unique_lock<std::mutex> lock(mLock);
-    mCond.wait(lock, [this, state] { return (mState == state); });
-}
-
-void Event::changeState(Event::EventState state) {
-    std::unique_lock<std::mutex> lock(mLock);
-    mState = state;
-    lock.unlock();
-
-    mCond.notify_one();
-}
-
-Increment::IncrementCase Event::getIncrementType() {
-    return mIncrementType;
-}
diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h
deleted file mode 100644
index 09a7c24..0000000
--- a/cmds/surfacereplayer/replayer/Event.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_EVENT_H
-#define ANDROID_SURFACEREPLAYER_EVENT_H
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <condition_variable>
-#include <mutex>
-
-namespace android {
-
-using Increment = surfaceflinger::Increment;
-
-class Event {
-  public:
-    Event(Increment::IncrementCase);
-
-    enum class EventState {
-        SettingUp,  // Completing as much time-independent work as possible
-        Waiting,    // Waiting for signal from main thread to finish execution
-        Signaled,   // Signaled by main thread, about to immediately switch to Running
-        Running     // Finishing execution of rest of work
-    };
-
-    void readyToExecute();
-    void complete();
-
-    Increment::IncrementCase getIncrementType();
-
-  private:
-    void waitUntil(EventState state);
-    void changeState(EventState state);
-
-    std::mutex mLock;
-    std::condition_variable mCond;
-
-    EventState mState = EventState::SettingUp;
-
-    Increment::IncrementCase mIncrementType;
-};
-}
-#endif
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp
deleted file mode 100644
index fbfcacf..0000000
--- a/cmds/surfacereplayer/replayer/Main.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * Replayer - Main.cpp
- *
- * 1. Get flags from command line
- * 2. Commit actions or settings based on the flags
- * 3. Initalize a replayer object with the filename passed in
- * 4. Replay
- * 5. Exit successfully or print error statement
- */
-
-#include <Replayer.h>
-
-#include <csignal>
-#include <iostream>
-#include <stdlib.h>
-#include <unistd.h>
-
-using namespace android;
-
-void printHelpMenu() {
-    std::cout << "SurfaceReplayer options:\n";
-    std::cout << "Usage: surfacereplayer [OPTIONS...] <TRACE FILE>\n";
-    std::cout << "  File path must be absolute" << std::endl << std::endl;
-
-    std::cout << "  -m  Stops the replayer at the start of the trace and switches ";
-                 "to manual replay\n";
-
-    std::cout << "\n  -t [Number of Threads]  Specifies the number of threads to be used while "
-                 "replaying (default is " << android::DEFAULT_THREADS << ")\n";
-
-    std::cout << "\n  -s [Timestamp]  Specify at what timestamp should the replayer switch "
-                 "to manual replay\n";
-
-    std::cout << "  -n  Ignore timestamps and run through trace as fast as possible\n";
-
-    std::cout << "  -l  Indefinitely loop the replayer\n";
-
-    std::cout << "  -h  Display help menu\n";
-
-    std::cout << std::endl;
-}
-
-int main(int argc, char** argv) {
-    std::string filename;
-    bool loop = false;
-    bool wait = true;
-    bool pauseBeginning = false;
-    int numThreads = DEFAULT_THREADS;
-    long stopHere = -1;
-
-    int opt = 0;
-    while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) {
-        switch (opt) {
-            case 'm':
-                pauseBeginning = true;
-                break;
-            case 't':
-                numThreads = atoi(optarg);
-                break;
-            case 's':
-                stopHere = atol(optarg);
-                break;
-            case 'n':
-                wait = false;
-                break;
-            case 'l':
-                loop = true;
-                break;
-            case 'h':
-            case '?':
-                printHelpMenu();
-                exit(0);
-            default:
-                std::cerr << "Invalid argument...exiting" << std::endl;
-                printHelpMenu();
-                exit(0);
-        }
-    }
-
-    char** input = argv + optind;
-    if (input[0] == nullptr) {
-        std::cerr << "No trace file provided...exiting" << std::endl;
-        abort();
-    }
-    filename.assign(input[0]);
-
-    status_t status = NO_ERROR;
-    do {
-        android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere);
-        status = r.replay();
-    } while(loop);
-
-    if (status == NO_ERROR) {
-        std::cout << "Successfully finished replaying trace" << std::endl;
-    } else {
-        std::cerr << "Trace replayer returned error: " << status << std::endl;
-    }
-
-    return 0;
-}
diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md
deleted file mode 100644
index 893f0dc..0000000
--- a/cmds/surfacereplayer/replayer/README.md
+++ /dev/null
@@ -1,262 +0,0 @@
-SurfaceReplayer Documentation
-===================
-
-[go/SurfaceReplayer](go/SurfaceReplayer)
-
-SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by
-[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays
-
-* Creation and deletion of surfaces/displays
-* Alterations to the surfaces/displays called Transactions
-* Buffer Updates to surfaces
-* VSync events
-
-At their specified times to be as close to the original trace.
-
-Usage
---------
-
-###Creating a trace
-
-SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to
-utilize it. To allow it to write to the device, run
-
-`setenforce 0`
-
-To start recording a trace, run
-
-`service call SurfaceFlinger 1020 i32 1`
-
-To stop recording, run
-
-`service call SurfaceFlinger 1020 i32 0`
-
-The default location for the trace is `/data/SurfaceTrace.dat`
-
-###Executable
-
-To replay a specific trace, execute
-
-`/data/local/tmp/surfacereplayer /absolute/path/to/trace`
-
-inside the android shell. This will replay the full trace and then exit. Running this command
-outside of the shell by prepending `adb shell` will not allow for manual control and will not turn
-off VSync injections if it interrupted in any way other than fully replaying the trace
-
-The replay will not fill surfaces with their contents during the capture. Rather they are given a
-random color which will be the same every time the trace is replayed. Surfaces modulate their color
-at buffer updates.
-
-**Options:**
-
-- -m    pause the replayer at the start of the trace for manual replay
-- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3)
-- -s [Timestamp] switches to manual replay at specified timestamp
-- -n    Ignore timestamps and run through trace as fast as possible
-- -l    Indefinitely loop the replayer
-- -h    displays help menu
-
-**Manual Replay:**
-When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled
-by the user. Pressing CTRL-C again will exit the replayer.
-
-Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to
-input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter
-without inputting a command repeats the previous command.
-
-- n  - steps the replayer to the next VSync event
-- ni - steps the replayer to the next increment
-- c  - continues normal replaying
-- c [milliseconds] - continue until specified number of milliseconds have passed
-- s [timestamp]    - continue and stop at specified timestamp
-- l  - list out timestamp of current increment
-- h  - displays help menu
-
-###Shared Library
-
-To use the shared library include these shared libraries
-
-`libsurfacereplayer`
-`libprotobuf-cpp-full`
-`libutils`
-
-And the static library
-
-`libtrace_proto`
-
-Include the replayer header at the top of your file
-
-`#include <replayer/Replayer.h>`
-
-There are two constructors for the replayer
-
-`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)`
-`Replayer(Trace& trace, ... ditto ...)`
-
-The first constructor takes in the filepath where the trace is located and loads in the trace
-object internally.
-- replayManually - **True**: if the replayer will immediately switch to manual replay at the start
-- numThreads - Number of worker threads the replayer will use.
-- wait - **False**: Replayer ignores waits in between increments
-- stopHere - Time stamp of where the replayer should run to then switch to manual replay
-
-The second constructor includes all of the same parameters but takes in a preloaded trace object.
-To use add
-
-`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>`
-
-To your file
-
-After initializing the Replayer call
-
-    replayer.replay();
-
-And the trace will start replaying. Once the trace is finished replaying, the function will return.
-The layers that are visible at the end of the trace will remain on screen until the program
-terminates.
-
-
-**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was
-never executed. This can be fixed by executing
-
-`service call SurfaceFlinger 23 i32 0`
-
-in the android shell
-
-Code Breakdown
--------------
-
-The Replayer is composed of 5 components.
-
-- The data format of the trace (Trace.proto)
-- The Replayer object (Replayer.cpp)
-- The synchronization mechanism to signal threads within the Replayer (Event.cpp)
-- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp)
-- The Main executable (Main.cpp)
-
-### Traces
-
-Traces are represented as a protobuf message located in surfacereplayer/proto/src.
-
-**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger).
-**Increments** contain the time stamp of when it occurred and a *oneof* which can be a
-
- - Transaction
- - SurfaceCreation
- - SurfaceDeletion
- - DisplayCreation
- - DisplayDeleteion
- - BufferUpdate
- - VSyncEvent
- - PowerModeUpdate
-
-**Transactions** contain whether the transaction was synchronous or animated and *repeated*
-**SurfaceChanges** and **DisplayChanges**
-
-- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as
-position, alpha, hidden, size, etc.
-- **DisplayChanges** contain the id of the display being manipulated and can be changes such as
-size, layer stack, projection, etc.
-
-**Surface/Display Creation** contain the id of the surface/display and the name of the
-surface/display
-
-**Surface/Display Deletion** contain the id of the surface/display to be deleted
-
-**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the
-buffer, and the frame number.
-
-**VSyncEvents** contain when the VSync event has occurred.
-
-**PowerModeUpdates** contain the id of the display being updated and what mode it is being
-changed to.
-
-To output the contents of a trace in a readable format, execute
-
-`**aprotoc** --decode=Trace \
--I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \
-$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \
- < **YourTraceFile.dat** > **YourOutputName.txt**`
-
-
-###Replayer
-
-Fundamentally the replayer loads a trace and iterates through each increment, waiting the required
-amount of time until the increment should be executed, then executing the increment. The first
-increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and
-goes from there.
-
-Increments from the trace are played asynchronously rather than one by one, being dispatched by
-the main thread, queued up in a thread pool and completed when the main thread deems they are
-ready to finish execution.
-
-When an increment is dispatched, it completes as much work as it can before it has to be
-synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action
-(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event
-object. The main thread holds a queue of these Event objects and completes the
-corresponding Event base on its time stamp. After completing an increment, the main thread will
-dispatch another increment and continue.
-
-The main thread's execution flow is outlined below
-
-    initReplay() //queue up the initial increments
-    while(!pendingIncrements.empty()) { //while increments remaining
-        event = pendingIncrement.pop();
-        wait(event.time_stamp(); //waitUntil it is time to complete this increment
-
-        event.complete() //signal to let event finish
-        if(increments remaing()) {
-            dispatchEvent() //queue up another increment
-        }
-    }
-
-A worker thread's flow looks like so
-
-    //dispatched!
-    Execute non-time sensitive work here
-    ...
-    event.readyToExecute() //time sensitive point...waiting for Main Thread
-    ...
-    Finish execution
-
-
-### Event
-
-An Event is a simple synchronization mechanism used to facilitate communication between the main
-and worker threads. Every time an increment is dispatched, an Event object is also created.
-
-An Event can be in 4 different states:
-
-- **SettingUp** - The worker is in the process of completing all non-time sensitive work
-- **Waiting** - The worker is waiting on the main thread to signal it.
-- **Signaled** - The worker has just been signaled by the main thread
-- **Running** - The worker is running again and finishing the rest of its work.
-
-When the main thread wants to finish the execution of a worker, the worker can either still be
-**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the
-main thread will **Signal** it to complete. The worker thread changes itself to the **Running**
-state once **Signaled**. This last step exists in order to communicate back to the main thread that
-the worker thread has actually started completing its execution, rather than being preempted right
-after signalling. Once this happens, the main thread schedules the next worker. This makes sure
-there is a constant amount of workers running at one time.
-
-This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the
-worker and main thread respectively.
-
-### BufferQueueScheduler
-
-During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a
-buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential
-**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it,
-which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by
-handling when **BufferUpdates** should be scheduled, making sure that they don't overlap.
-
-When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a
-**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one
-every time an Event is completed.
-
-### Main
-
-The main exectuable reads in the command line arguments. Creates the Replayer using those
-arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit
-gracefully, if there are then it will report the error and then exit.
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 3f7c7d6..44235cc 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -464,7 +464,6 @@
 void Replayer::setSize(SurfaceComposerClient::Transaction& t,
         layer_id id, const SizeChange& sc) {
     ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h());
-    t.setSize(mLayers[id], sc.w(), sc.h());
 }
 
 void Replayer::setLayer(SurfaceComposerClient::Transaction& t,
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
deleted file mode 100644
index d62522a..0000000
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_H
-#define ANDROID_SURFACEREPLAYER_H
-
-#include "BufferQueueScheduler.h"
-#include "Color.h"
-#include "Event.h"
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceControl.h>
-
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-
-#include <stdatomic.h>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <thread>
-#include <unordered_map>
-#include <utility>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
-const auto RAND_COLOR_SEED = 700;
-const auto DEFAULT_THREADS = 3;
-
-typedef int32_t layer_id;
-typedef int32_t display_id;
-
-typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
-typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
-
-class Replayer {
-  public:
-    Replayer(const std::string& filename, bool replayManually = false,
-            int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
-    Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
-            bool wait = true, nsecs_t stopHere = -1);
-
-    status_t replay();
-
-  private:
-    status_t initReplay();
-
-    void waitForConsoleCommmand();
-    static void stopAutoReplayHandler(int signal);
-
-    status_t dispatchEvent(int index);
-
-    status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
-    status_t createSurfaceControl(const SurfaceCreation& create,
-            const std::shared_ptr<Event>& event);
-    status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
-    void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
-    void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
-    void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
-
-    status_t doSurfaceTransaction(SurfaceComposerClient::Transaction& transaction,
-            const SurfaceChanges& surfaceChange);
-    void doDisplayTransaction(SurfaceComposerClient::Transaction& transaction,
-            const DisplayChanges& displayChange);
-
-    void setPosition(SurfaceComposerClient::Transaction& t,
-            layer_id id, const PositionChange& pc);
-    void setSize(SurfaceComposerClient::Transaction& t,
-            layer_id id, const SizeChange& sc);
-    void setAlpha(SurfaceComposerClient::Transaction& t,
-            layer_id id, const AlphaChange& ac);
-    void setLayer(SurfaceComposerClient::Transaction& t,
-            layer_id id, const LayerChange& lc);
-    void setCrop(SurfaceComposerClient::Transaction& t,
-            layer_id id, const CropChange& cc);
-    void setCornerRadius(SurfaceComposerClient::Transaction& t,
-            layer_id id, const CornerRadiusChange& cc);
-    void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
-            layer_id id, const BackgroundBlurRadiusChange& cc);
-    void setBlurRegions(SurfaceComposerClient::Transaction& t,
-            layer_id id, const BlurRegionsChange& cc);
-    void setMatrix(SurfaceComposerClient::Transaction& t,
-            layer_id id, const MatrixChange& mc);
-    void setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
-            layer_id id, const TransparentRegionHintChange& trgc);
-    void setLayerStack(SurfaceComposerClient::Transaction& t,
-            layer_id id, const LayerStackChange& lsc);
-    void setHiddenFlag(SurfaceComposerClient::Transaction& t,
-            layer_id id, const HiddenFlagChange& hfc);
-    void setOpaqueFlag(SurfaceComposerClient::Transaction& t,
-            layer_id id, const OpaqueFlagChange& ofc);
-    void setSecureFlag(SurfaceComposerClient::Transaction& t,
-            layer_id id, const SecureFlagChange& sfc);
-    void setReparentChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const ReparentChange& c);
-    void setRelativeParentChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const RelativeParentChange& c);
-    void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const ShadowRadiusChange& c);
-    void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const BlurRegionsChange& c);
-
-    void setDisplaySurface(SurfaceComposerClient::Transaction& t,
-            display_id id, const DispSurfaceChange& dsc);
-    void setDisplayLayerStack(SurfaceComposerClient::Transaction& t,
-            display_id id, const LayerStackChange& lsc);
-    void setDisplaySize(SurfaceComposerClient::Transaction& t,
-            display_id id, const SizeChange& sc);
-    void setDisplayProjection(SurfaceComposerClient::Transaction& t,
-            display_id id, const ProjectionChange& pc);
-
-    void waitUntilTimestamp(int64_t timestamp);
-    status_t loadSurfaceComposerClient();
-
-    Trace mTrace;
-    bool mLoaded = false;
-    int32_t mIncrementIndex = 0;
-    int64_t mCurrentTime = 0;
-    int32_t mNumThreads = DEFAULT_THREADS;
-
-    Increment mCurrentIncrement;
-
-    std::string mLastInput;
-
-    static atomic_bool sReplayingManually;
-    bool mWaitingForNextVSync;
-    bool mWaitForTimeStamps;
-    nsecs_t mStopTimeStamp;
-    bool mHasStopped;
-
-    std::mutex mLayerLock;
-    std::condition_variable mLayerCond;
-    std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
-    std::unordered_map<layer_id, HSV> mColors;
-
-    std::mutex mPendingLayersLock;
-    std::vector<layer_id> mLayersPendingRemoval;
-
-    std::mutex mBufferQueueSchedulerLock;
-    std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
-
-    std::mutex mDisplayLock;
-    std::condition_variable mDisplayCond;
-    std::unordered_map<display_id, sp<IBinder>> mDisplays;
-
-    sp<SurfaceComposerClient> mComposerClient;
-    std::queue<std::shared_ptr<Event>> mPendingIncrements;
-};
-
-}  // namespace android
-#endif
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
deleted file mode 100644
index 58bfbf3..0000000
--- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
+++ /dev/null
@@ -1,285 +0,0 @@
-#!/usr/bin/python
-from subprocess import call
-import os
-proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/"
-call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"])
-
-from trace_pb2 import *
-
-trace = Trace()
-
-def main():
-    global trace
-    while(1):
-        option = main_menu()
-
-        if option == 0:
-            break
-
-        increment = trace.increment.add()
-        increment.time_stamp  = int(input("Time stamp of action: "))
-
-        if option == 1:
-           transaction(increment)
-        elif option == 2:
-            surface_create(increment)
-        elif option == 3:
-            surface_delete(increment)
-        elif option == 4:
-            display_create(increment)
-        elif option == 5:
-            display_delete(increment)
-        elif option == 6:
-            buffer_update(increment)
-        elif option == 7:
-            vsync_event(increment)
-        elif option == 8:
-            power_mode_update(increment)
-
-    seralizeTrace()
-
-def seralizeTrace():
-    with open("trace.dat", 'wb') as f:
-        f.write(trace.SerializeToString())
-
-
-def main_menu():
-    print ("")
-    print ("What would you like to do?")
-    print ("1. Add transaction")
-    print ("2. Add surface creation")
-    print ("3. Add surface deletion")
-    print ("4. Add display creation")
-    print ("5. Add display deletion")
-    print ("6. Add buffer update")
-    print ("7. Add VSync event")
-    print ("8. Add power mode update")
-    print ("0. Finish and serialize")
-    print ("")
-
-    return int(input("> "))
-
-def transaction_menu():
-    print ("")
-    print ("What kind of transaction?")
-    print ("1. Position Change")
-    print ("2. Size Change")
-    print ("3. Alpha Change")
-    print ("4. Layer Change")
-    print ("5. Crop Change")
-    print ("6. Final Crop Change")
-    print ("7. Matrix Change")
-    print ("9. Transparent Region Hint Change")
-    print ("10. Layer Stack Change")
-    print ("11. Hidden Flag Change")
-    print ("12. Opaque Flag Change")
-    print ("13. Secure Flag Change")
-    print ("14. Deferred Transaction Change")
-    print ("15. Display - Surface Change")
-    print ("16. Display - Layer Stack Change")
-    print ("17. Display - Size Change")
-    print ("18. Display - Projection Change")
-    print ("0. Finished adding Changes to this transaction")
-    print ("")
-
-    return int(input("> "))
-
-def transaction(increment):
-    global trace
-
-    increment.transaction.synchronous \
-            = bool(input("Is transaction synchronous (True/False): "))
-    increment.transaction.animation \
-            = bool(input("Is transaction animated (True/False): "))
-
-    while(1):
-        option = transaction_menu()
-
-        if option == 0:
-            break
-
-        change = None
-        if option <= 14:
-            change = increment.transaction.surface_change.add()
-        elif option >= 15 and option <= 18:
-            change = increment.transaction.display_change.add()
-
-        change.id = int(input("ID of layer/display to undergo a change: "))
-
-        if option == 1:
-            change.position.x, change.position.y = position()
-        elif option == 2:
-            change.size.w, change.size.h = size()
-        elif option == 3:
-            change.alpha.alpha = alpha()
-        elif option == 4:
-            change.layer.layer = layer()
-        elif option == 5:
-            change.crop.rectangle.left,  change.crop.rectangle.top, \
-            change.crop.rectangle.right, change.crop.rectangle.bottom = crop()
-        elif option == 6:
-            change.final_crop.rectangle.left, \
-            change.final_crop.rectangle.top,  \
-            change.final_crop.rectangle.right,\
-            change.final_crop.rectangle.bottom = final_crop()
-        elif option == 7:
-            change.matrix.dsdx,\
-            change.matrix.dtdx,\
-            change.matrix.dsdy,\
-            change.matrix.dtdy = layer()
-        elif option == 9:
-            for rect in transparent_region_hint():
-                new = increment.transparent_region_hint.region.add()
-                new.left = rect[0]
-                new.top = rect[1]
-                new.right = rect[2]
-                new.bottom = rect[3]
-        elif option == 10:
-            change.layer_stack.layer_stack = layer_stack()
-        elif option == 11:
-            change.hidden_flag.hidden_flag = hidden_flag()
-        elif option == 12:
-            change.opaque_flag.opaque_flag = opaque_flag()
-        elif option == 13:
-            change.secure_flag.secure_flag = secure_flag()
-        elif option == 14:
-            change.deferred_transaction.layer_id, \
-            change.deferred_transaction.frame_number = deferred_transaction()
-        elif option == 15:
-            change.surface.buffer_queue_id, \
-            change.surface.buffer_queue_name = surface()
-        elif option == 16:
-            change.layer_stack.layer_stack = layer_stack()
-        elif option == 17:
-            change.size.w, change.size.h = size()
-        elif option == 18:
-            projection(change)
-
-def surface_create(increment):
-    increment.surface_creation.id = int(input("Enter id: "))
-    n = str(raw_input("Enter name: "))
-    increment.surface_creation.name = n
-    increment.surface_creation.w = input("Enter w: ")
-    increment.surface_creation.h = input("Enter h: ")
-
-def surface_delete(increment):
-    increment.surface_deletion.id = int(input("Enter id: "))
-
-def display_create(increment):
-    increment.display_creation.id = int(input("Enter id: "))
-    increment.display_creation.name = str(raw_input("Enter name: "))
-    increment.display_creation.display_id = int(input("Enter display ID: "))
-    increment.display_creation.is_secure = bool(input("Enter if secure: "))
-
-def display_delete(increment):
-    increment.surface_deletion.id = int(input("Enter id: "))
-
-def buffer_update(increment):
-    increment.buffer_update.id = int(input("Enter id: "))
-    increment.buffer_update.w = int(input("Enter w: "))
-    increment.buffer_update.h = int(input("Enter h: "))
-    increment.buffer_update.frame_number = int(input("Enter frame_number: "))
-
-def vsync_event(increment):
-    increment.vsync_event.when = int(input("Enter when: "))
-
-def power_mode_update(increment):
-    increment.power_mode_update.id = int(input("Enter id: "))
-    increment.power_mode_update.mode = int(input("Enter mode: "))
-
-def position():
-    x = input("Enter x: ")
-    y = input("Enter y: ")
-
-    return float(x), float(y)
-
-def size():
-    w = input("Enter w: ")
-    h = input("Enter h: ")
-
-    return int(w), int(h)
-
-def alpha():
-    alpha = input("Enter alpha: ")
-
-    return float(alpha)
-
-def layer():
-    layer = input("Enter layer: ")
-
-    return int(layer)
-
-def crop():
-    return rectangle()
-
-def final_crop():
-    return rectangle()
-
-def matrix():
-    dsdx = input("Enter dsdx: ")
-    dtdx = input("Enter dtdx: ")
-    dsdy = input("Enter dsdy: ")
-    dtdy = input("Enter dtdy: ")
-
-    return float(dsdx)
-
-def transparent_region_hint():
-    num = input("Enter number of rectangles in region: ")
-
-    return [rectangle() in range(x)]
-
-def layer_stack():
-    layer_stack = input("Enter layer stack: ")
-
-    return int(layer_stack)
-
-def hidden_flag():
-    flag = input("Enter hidden flag state (True/False): ")
-
-    return bool(flag)
-
-def opaque_flag():
-    flag = input("Enter opaque flag state (True/False): ")
-
-    return bool(flag)
-
-def secure_flag():
-    flag = input("Enter secure flag state (True/False): ")
-
-    return bool(flag)
-
-def deferred_transaction():
-    layer_id = input("Enter layer_id: ")
-    frame_number = input("Enter frame_number: ")
-
-    return int(layer_id), int(frame_number)
-
-def surface():
-    id = input("Enter id: ")
-    name = raw_input("Enter name: ")
-
-    return int(id), str(name)
-
-def projection(change):
-    change.projection.orientation = input("Enter orientation: ")
-    print("Enter rectangle for viewport")
-    change.projection.viewport.left, \
-    change.projection.viewport.top,  \
-    change.projection.viewport.right,\
-    change.projection.viewport.bottom = rectangle()
-    print("Enter rectangle for frame")
-    change.projection.frame.left, \
-    change.projection.frame.top,  \
-    change.projection.frame.right,\
-    change.projection.frame.bottom = rectangle()
-
-def rectangle():
-    left = input("Enter left: ")
-    top = input("Enter top: ")
-    right = input("Enter right: ")
-    bottom = input("Enter bottom: ")
-
-    return int(left), int(top), int(right), int(bottom)
-
-if __name__ == "__main__":
-    main()
diff --git a/data/etc/android.software.credentials.xml b/data/etc/android.software.credentials.xml
new file mode 100644
index 0000000..234d144
--- /dev/null
+++ b/data/etc/android.software.credentials.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<permissions>
+    <feature name="android.software.credentials" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 8fdd8d0..0833012 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -52,6 +52,7 @@
     <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
+    <feature name="android.software.credentials" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
     <feature name="android.software.window_magnification" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 59d5b10..6af4d91 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -52,6 +52,7 @@
     <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
+    <feature name="android.software.credentials" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
     <feature name="android.software.window_magnification" />
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 63aa7ff..cd8e63d 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -16,6 +16,28 @@
 
 /**
  * @addtogroup Choreographer
+ *
+ * Choreographer coordinates the timing of frame rendering. This is the C version of the
+ * android.view.Choreographer object in Java.
+ *
+ * As of API level 33, apps can follow proper frame pacing and even choose a future frame to render.
+ * The API is used as follows:
+ * 1. The app posts an {@link AChoreographer_vsyncCallback} to Choreographer to run on the next
+ * frame.
+ * 2. The callback is called when it is the time to start the frame with an {@link
+ * AChoreographerFrameCallbackData} payload: information about multiple possible frame
+ * timelines.
+ * 3. Apps can choose a frame timeline from the {@link
+ * AChoreographerFrameCallbackData} payload, depending on the frame deadline they can meet when
+ * rendering the frame and their desired presentation time, and subsequently
+ * {@link ASurfaceTransaction_setFrameTimeline notify SurfaceFlinger}
+ * of the choice. Alternatively, for apps that do not choose a frame timeline, their frame would be
+ * presented at the earliest possible timeline.
+ *   - The preferred frame timeline is the default frame
+ * timeline that the platform scheduled for the app, based on device configuration.
+ * 4. SurfaceFlinger attempts to follow the chosen frame timeline, by not applying transactions or
+ * latching buffers before the desired presentation time.
+ *
  * @{
  */
 
@@ -47,7 +69,8 @@
 
 struct AChoreographerFrameCallbackData;
 /**
- * Opaque type that provides access to an AChoreographerFrameCallbackData object.
+ * Opaque type that provides access to an AChoreographerFrameCallbackData object, which contains
+ * various methods to extract frame information.
  */
 typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData;
 
@@ -73,8 +96,9 @@
 
 /**
  * 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.
+ * It is called with \c callbackData describing multiple frame timelines, as well as the \c data
+ * pointer provided by the application that registered a callback. The \c callbackData does not
+ * outlive the callback.
  */
 typedef void (*AChoreographer_vsyncCallback)(
         const AChoreographerFrameCallbackData* callbackData, void* data);
@@ -110,7 +134,7 @@
         __DEPRECATED_IN(29);
 
 /**
- * Power a callback to be run on the next frame.  The data pointer provided will
+ * Post a callback to be run on the next frame.  The data pointer provided will
  * be passed to the callback function when it's called.
  *
  * Available since API level 29.
@@ -131,8 +155,10 @@
                                                uint32_t delayMillis) __INTRODUCED_IN(29);
 
 /**
- * Posts a callback to run on the next frame. The data pointer provided will
+ * Posts a callback to be run on the next frame. The data pointer provided will
  * be passed to the callback function when it's called.
+ *
+ * Available since API level 33.
  */
 void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
                                         AChoreographer_vsyncCallback callback, void* data)
@@ -189,7 +215,10 @@
         __INTRODUCED_IN(30);
 
 /**
- * The time in nanoseconds when the frame started being rendered.
+ * The time in nanoseconds at which the frame started being rendered.
+ *
+ * Note that this time should \b not be used to advance animation clocks.
+ * Instead, see AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos().
  */
 int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
         const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
@@ -201,25 +230,38 @@
         const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
 
 /**
- * Get index of the platform-preferred FrameTimeline.
+ * Gets the index of the platform-preferred frame timeline.
+ * The preferred frame timeline is the default
+ * by which the platform scheduled the app, based on the device configuration.
  */
 size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
         const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
 
 /**
- * The vsync ID token used to map Choreographer data.
+ * Gets the token used by the platform to identify the frame timeline at the given \c index.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
  */
 AVsyncId 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.
+ * Gets the time in nanoseconds at which the frame described at the given \c index is expected to
+ * be presented. This time should be used to advance any animation clocks.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
  */
 int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
 
 /**
- * The time in nanoseconds which the frame at given index needs to be ready by.
+ * Gets the time in nanoseconds at which the frame described at the given \c index needs to be
+ * ready by in order to be presented on time.
+ *
+ * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
+ * AChoreographerFrameCallbackData_getFrameTimelinesLength()
  */
 int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
diff --git a/include/android/input.h b/include/android/input.h
index 8cd9e95..d906af6 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -772,8 +772,17 @@
      */
     AMOTION_EVENT_AXIS_GENERIC_16 = 47,
 
+    /**
+     * Note: This is not an "Axis constant". It does not represent any axis, nor should it be used
+     * to represent any axis. It is a constant holding the value of the largest defined axis value,
+     * to make some computations (like iterating through all possible axes) cleaner.
+     * Please update the value accordingly if you add a new axis.
+     */
+    AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GENERIC_16,
+
     // NOTE: If you add a new axis here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
+    //       Update AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE accordingly as well.
 };
 
 /**
@@ -840,6 +849,12 @@
      * This classification type should be used to accelerate the long press behaviour.
      */
     AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS = 2,
+    /**
+     * Classification constant: touchpad two-finger swipe.
+     *
+     * The current event stream represents the user swiping with two fingers on a touchpad.
+     */
+    AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE = 3,
 };
 
 /**
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 214559d..e5b5db2 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -776,7 +776,59 @@
     AKEYCODE_THUMBS_DOWN = 287,
     /** Used to switch current account that is consuming content.
      * May be consumed by system to switch current viewer profile. */
-    AKEYCODE_PROFILE_SWITCH = 288
+    AKEYCODE_PROFILE_SWITCH = 288,
+    /** Video Application key #1. */
+    AKEYCODE_VIDEO_APP_1 = 289,
+    /** Video Application key #2. */
+    AKEYCODE_VIDEO_APP_2 = 290,
+    /** Video Application key #3. */
+    AKEYCODE_VIDEO_APP_3 = 291,
+    /** Video Application key #4. */
+    AKEYCODE_VIDEO_APP_4 = 292,
+    /** Video Application key #5. */
+    AKEYCODE_VIDEO_APP_5 = 293,
+    /** Video Application key #6. */
+    AKEYCODE_VIDEO_APP_6 = 294,
+    /** Video Application key #7. */
+    AKEYCODE_VIDEO_APP_7 = 295,
+    /** Video Application key #8. */
+    AKEYCODE_VIDEO_APP_8 = 296,
+    /** Featured Application key #1. */
+    AKEYCODE_FEATURED_APP_1 = 297,
+    /** Featured Application key #2. */
+    AKEYCODE_FEATURED_APP_2 = 298,
+    /** Featured Application key #3. */
+    AKEYCODE_FEATURED_APP_3 = 299,
+    /** Featured Application key #4. */
+    AKEYCODE_FEATURED_APP_4 = 300,
+    /** Demo Application key #1. */
+    AKEYCODE_DEMO_APP_1 = 301,
+    /** Demo Application key #2. */
+    AKEYCODE_DEMO_APP_2 = 302,
+    /** Demo Application key #3. */
+    AKEYCODE_DEMO_APP_3 = 303,
+    /** Demo Application key #4. */
+    AKEYCODE_DEMO_APP_4 = 304,
+    /** Keyboard backlight Down key.
+     * Adjusts the keyboard backlight brightness down. */
+    AKEYCODE_KEYBOARD_BACKLIGHT_DOWN = 305,
+    /** Keyboard backlight Up key.
+     * Adjusts the keyboard backlight brightness up. */
+    AKEYCODE_KEYBOARD_BACKLIGHT_UP = 306,
+    /** Keyboard backlight Toggle key.
+     * Toggles the keyboard backlight on/off. */
+    AKEYCODE_KEYBOARD_BACKLIGHT_TOGGLE = 307,
+    /** The primary button on the barrel of a stylus.
+     * This is usually the button closest to the tip of the stylus. */
+    AKEYCODE_STYLUS_BUTTON_PRIMARY = 308,
+    /** The secondary button on the barrel of a stylus.
+     * This is usually the second button from the tip of the stylus. */
+    AKEYCODE_STYLUS_BUTTON_SECONDARY = 309,
+    /** The tertiary button on the barrel of a stylus.
+     * This is usually the third button from the tip of the stylus. */
+    AKEYCODE_STYLUS_BUTTON_TERTIARY = 310,
+    /** A button on the tail end of a stylus. */
+    AKEYCODE_STYLUS_BUTTON_TAIL = 311,
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 9a36ecb..f76e73d 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -545,6 +545,8 @@
  * You can register for changes in the refresh rate using
  * \a AChoreographer_registerRefreshRateCallback.
  *
+ * See ASurfaceTransaction_clearFrameRate().
+ *
  * \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special
  * value that indicates the app will accept the system's choice for the display frame rate, which is
  * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
@@ -568,6 +570,31 @@
                                       __INTRODUCED_IN(31);
 
 /**
+ * Clears the frame rate which is set for \a surface_control.
+ *
+ * This is equivalent to calling
+ * ASurfaceTransaction_setFrameRateWithChangeStrategy(
+ * transaction, 0, compatibility, changeFrameRateStrategy).
+ *
+ * Usage of this API won't directly affect the application's frame production pipeline. However,
+ * because the system may change the display refresh rate, calls to this function may result in
+ * changes to Choreographer callback timings, and changes to the time interval at which the system
+ * releases buffers back to the application.
+ *
+ * See ASurfaceTransaction_setFrameRateWithChangeStrategy()
+ *
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
+ * See ASurfaceTransaction_setFrameRateWithChangeStrategy().
+ *
+ * Available since API level 34.
+ */
+void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* transaction,
+                                        ASurfaceControl* surface_control)
+                                        __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
  * Indicate whether to enable backpressure for buffer submission to a given SurfaceControl.
  *
  * By default backpressure is disabled, which means submitting a buffer prior to receiving
@@ -597,20 +624,20 @@
                                                __INTRODUCED_IN(31);
 
 /**
- * Sets the frame timeline to use in Surface Flinger.
+ * Sets the frame timeline to use in SurfaceFlinger.
  *
- * A frame timeline should be chosen based on what frame deadline the application
- * can meet when rendering the frame and the application's desired present time.
- * By setting a frame timeline, Surface Flinger tries to present the frame at the corresponding
- * expected present time.
+ * A frame timeline should be chosen based on the frame deadline the application
+ * can meet when rendering the frame and the application's desired presentation time.
+ * By setting a frame timeline, SurfaceFlinger tries to present the frame at the corresponding
+ * expected presentation time.
  *
  * To receive frame timelines, a callback must be posted to Choreographer using
- * AChoreographer_postExtendedFrameCallback(). The \a vsnycId can then be extracted from the
+ * AChoreographer_postVsyncCallback(). The \c vsyncId can then be extracted from the
  * callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId().
  *
- * \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. A stale or
- * invalid value will be ignored.
+ * \param vsyncId The vsync ID received from AChoreographer, setting the frame's presentation target
+ * to the corresponding expected presentation time and deadline from the frame to be rendered. A
+ * stale or invalid value will be ignored.
  */
 void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction,
                                           AVsyncId vsyncId) __INTRODUCED_IN(33);
diff --git a/include/android/surface_control_jni.h b/include/android/surface_control_jni.h
new file mode 100644
index 0000000..a0a1fdb
--- /dev/null
+++ b/include/android/surface_control_jni.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file surface_control_jni.h
+ */
+
+#ifndef ANDROID_SURFACE_CONTROL_JNI_H
+#define ANDROID_SURFACE_CONTROL_JNI_H
+
+#include <jni.h>
+#include <sys/cdefs.h>
+
+#include <android/surface_control.h>
+
+__BEGIN_DECLS
+
+/**
+ * Return the ASurfaceControl wrapped by a Java SurfaceControl object.
+ *
+ * The caller takes ownership of the returned ASurfaceControl returned and must
+ * release it * using ASurfaceControl_release.
+ *
+ * surfaceControlObj must be a non-null instance of android.view.SurfaceControl
+ * and isValid() must be true.
+ *
+ * Available since API level 34.
+ */
+ASurfaceControl* _Nonnull ASurfaceControl_fromSurfaceControl(JNIEnv* _Nonnull env,
+        jobject _Nonnull surfaceControlObj) __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
+ * Return the ASurfaceTransaction wrapped by a Java Transaction object.
+ *
+ * The returned ASurfaceTransaction is still owned by the Java Transaction object is only
+ * valid while the Java Transaction object is alive. In particular, the returned transaction
+ * must NOT be deleted with ASurfaceTransaction_delete.
+ *
+ * transactionObj must be a non-null instance of
+ * android.view.SurfaceControl.Transaction and close() must not already be called.
+ *
+ * Available since API level 34.
+ */
+ASurfaceTransaction* _Nonnull ASurfaceTransaction_fromTransaction(JNIEnv* _Nonnull env,
+        jobject _Nonnull transactionObj) __INTRODUCED_IN(__ANDROID_API_U__);
+
+__END_DECLS
+
+#endif // ANDROID_SURFACE_CONTROL_JNI_H
+/** @} */
diff --git a/include/attestation/HmacKeyManager.h b/include/attestation/HmacKeyManager.h
index 571a361..d725be1 100644
--- a/include/attestation/HmacKeyManager.h
+++ b/include/attestation/HmacKeyManager.h
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#ifndef ATTESTATION_HMACKEYMANAGER_H
+#define ATTESTATION_HMACKEYMANAGER_H
+
 #include <array>
 
 namespace android {
@@ -29,4 +32,6 @@
 private:
     const std::array<uint8_t, 128> mHmacKey;
 };
-} // namespace android
\ No newline at end of file
+} // namespace android
+
+#endif // ATTESTATION_HMACKEYMANAGER_H
\ No newline at end of file
diff --git a/include/ftl/algorithm.h b/include/ftl/algorithm.h
new file mode 100644
index 0000000..c5ff03b
--- /dev/null
+++ b/include/ftl/algorithm.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+
+#include <ftl/optional.h>
+
+namespace android::ftl {
+
+// Adapter for std::find_if that converts the return value from iterator to optional.
+//
+//   const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv};
+//   assert(ftl::find_if(vector, [](const auto& str) { return str.front() == 'c'; }) == "cake"sv);
+//
+template <typename Container, typename Predicate, typename V = typename Container::value_type>
+constexpr auto find_if(const Container& container, Predicate&& predicate)
+    -> Optional<std::reference_wrapper<const V>> {
+  const auto it = std::find_if(std::cbegin(container), std::cend(container),
+                               std::forward<Predicate>(predicate));
+  if (it == std::cend(container)) return {};
+  return std::cref(*it);
+}
+
+// Transformers for ftl::find_if on a map-like `Container` that contains key-value pairs.
+//
+//   const ftl::SmallMap map = ftl::init::map<int, ftl::StaticVector<std::string_view, 3>>(
+//       12, "snow"sv, "cone"sv)(13, "tiramisu"sv)(14, "upside"sv, "down"sv, "cake"sv);
+//
+//   using Map = decltype(map);
+//
+//   assert(14 == ftl::find_if(map, [](const auto& pair) {
+//                  return pair.second.size() == 3;
+//                }).transform(ftl::to_key<Map>));
+//
+//   const auto opt = ftl::find_if(map, [](const auto& pair) {
+//                      return pair.second.size() == 1;
+//                    }).transform(ftl::to_mapped_ref<Map>);
+//
+//   assert(opt);
+//   assert(opt->get() == ftl::StaticVector("tiramisu"sv));
+//
+template <typename Map, typename Pair = typename Map::value_type,
+          typename Key = typename Map::key_type>
+constexpr auto to_key(const Pair& pair) -> Key {
+  return pair.first;
+}
+
+template <typename Map, typename Pair = typename Map::value_type,
+          typename Mapped = typename Map::mapped_type>
+constexpr auto to_mapped_ref(const Pair& pair) -> std::reference_wrapper<const Mapped> {
+  return std::cref(pair.second);
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/details/match.h b/include/ftl/details/match.h
new file mode 100644
index 0000000..51b99d2
--- /dev/null
+++ b/include/ftl/details/match.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <variant>
+
+namespace android::ftl::details {
+
+template <typename... Ms>
+struct Matcher : Ms... {
+  using Ms::operator()...;
+};
+
+// Deduction guide.
+template <typename... Ms>
+Matcher(Ms...) -> Matcher<Ms...>;
+
+template <typename Matcher, typename... Ts>
+constexpr bool is_exhaustive_match_v = (std::is_invocable_v<Matcher, Ts> && ...);
+
+template <typename...>
+struct Match;
+
+template <typename T, typename U, typename... Ts>
+struct Match<T, U, Ts...> {
+  template <typename Variant, typename Matcher>
+  static decltype(auto) match(Variant& variant, const Matcher& matcher) {
+    if (auto* const ptr = std::get_if<T>(&variant)) {
+      return matcher(*ptr);
+    } else {
+      return Match<U, Ts...>::match(variant, matcher);
+    }
+  }
+};
+
+template <typename T>
+struct Match<T> {
+  template <typename Variant, typename Matcher>
+  static decltype(auto) match(Variant& variant, const Matcher& matcher) {
+    return matcher(std::get<T>(variant));
+  }
+};
+
+}  // namespace android::ftl::details
diff --git a/include/ftl/details/optional.h b/include/ftl/details/optional.h
new file mode 100644
index 0000000..bff7c1e
--- /dev/null
+++ b/include/ftl/details/optional.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <optional>
+
+#include <ftl/details/type_traits.h>
+
+namespace android::ftl {
+
+template <typename>
+struct Optional;
+
+namespace details {
+
+template <typename>
+struct is_optional : std::false_type {};
+
+template <typename T>
+struct is_optional<std::optional<T>> : std::true_type {};
+
+template <typename T>
+struct is_optional<Optional<T>> : std::true_type {};
+
+template <typename F, typename T>
+struct transform_result {
+  using type = Optional<std::remove_cv_t<std::invoke_result_t<F, T>>>;
+};
+
+template <typename F, typename T>
+using transform_result_t = typename transform_result<F, T>::type;
+
+template <typename F, typename T>
+struct and_then_result {
+  using type = remove_cvref_t<std::invoke_result_t<F, T>>;
+  static_assert(is_optional<type>{}, "and_then function must return an optional");
+};
+
+template <typename F, typename T>
+using and_then_result_t = typename and_then_result<F, T>::type;
+
+}  // namespace details
+}  // namespace android::ftl
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/include/ftl/details/type_traits.h
similarity index 70%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to include/ftl/details/type_traits.h
index 9fac3e8..7092ec5 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/include/ftl/details/type_traits.h
@@ -14,10 +14,14 @@
  * limitations under the License.
  */
 
-package android.gui;
+#pragma once
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
-}
+#include <type_traits>
+
+namespace android::ftl::details {
+
+// TODO: Replace with std::remove_cvref_t in C++20.
+template <typename U>
+using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+
+}  // namespace android::ftl::details
diff --git a/include/ftl/match.h b/include/ftl/match.h
new file mode 100644
index 0000000..7318c45
--- /dev/null
+++ b/include/ftl/match.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utility>
+#include <variant>
+
+#include <ftl/details/match.h>
+
+namespace android::ftl {
+
+// Concise alternative to std::visit that compiles to branches rather than a dispatch table. For
+// std::variant<T0, ..., TN> where N is small, this is slightly faster since the branches can be
+// inlined unlike the function pointers.
+//
+//   using namespace std::chrono;
+//   std::variant<seconds, minutes, hours> duration = 119min;
+//
+//   // Mutable match.
+//   ftl::match(duration, [](auto& d) { ++d; });
+//
+//   // Immutable match. Exhaustive due to minutes being convertible to seconds.
+//   assert("2 hours"s ==
+//          ftl::match(duration,
+//                     [](const seconds& s) {
+//                       const auto h = duration_cast<hours>(s);
+//                       return std::to_string(h.count()) + " hours"s;
+//                     },
+//                     [](const hours& h) { return std::to_string(h.count() / 24) + " days"s; }));
+//
+template <typename... Ts, typename... Ms>
+decltype(auto) match(std::variant<Ts...>& variant, Ms&&... matchers) {
+  const auto matcher = details::Matcher{std::forward<Ms>(matchers)...};
+  static_assert(details::is_exhaustive_match_v<decltype(matcher), Ts&...>, "Non-exhaustive match");
+
+  return details::Match<Ts...>::match(variant, matcher);
+}
+
+template <typename... Ts, typename... Ms>
+decltype(auto) match(const std::variant<Ts...>& variant, Ms&&... matchers) {
+  const auto matcher = details::Matcher{std::forward<Ms>(matchers)...};
+  static_assert(details::is_exhaustive_match_v<decltype(matcher), const Ts&...>,
+                "Non-exhaustive match");
+
+  return details::Match<Ts...>::match(variant, matcher);
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
new file mode 100644
index 0000000..626507f
--- /dev/null
+++ b/include/ftl/optional.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <optional>
+#include <utility>
+
+#include <ftl/details/optional.h>
+
+namespace android::ftl {
+
+// Superset of std::optional<T> with monadic operations, as proposed in https://wg21.link/P0798R8.
+//
+// TODO: Remove in C++23.
+//
+template <typename T>
+struct Optional final : std::optional<T> {
+  using std::optional<T>::optional;
+
+  // Implicit downcast.
+  Optional(std::optional<T> other) : std::optional<T>(std::move(other)) {}
+
+  using std::optional<T>::has_value;
+  using std::optional<T>::value;
+
+  // Returns Optional<U> where F is a function that maps T to U.
+  template <typename F>
+  constexpr auto transform(F&& f) const& {
+    using R = details::transform_result_t<F, decltype(value())>;
+    if (has_value()) return R(std::invoke(std::forward<F>(f), value()));
+    return R();
+  }
+
+  template <typename F>
+  constexpr auto transform(F&& f) & {
+    using R = details::transform_result_t<F, decltype(value())>;
+    if (has_value()) return R(std::invoke(std::forward<F>(f), value()));
+    return R();
+  }
+
+  template <typename F>
+  constexpr auto transform(F&& f) const&& {
+    using R = details::transform_result_t<F, decltype(std::move(value()))>;
+    if (has_value()) return R(std::invoke(std::forward<F>(f), std::move(value())));
+    return R();
+  }
+
+  template <typename F>
+  constexpr auto transform(F&& f) && {
+    using R = details::transform_result_t<F, decltype(std::move(value()))>;
+    if (has_value()) return R(std::invoke(std::forward<F>(f), std::move(value())));
+    return R();
+  }
+
+  // Returns Optional<U> where F is a function that maps T to Optional<U>.
+  template <typename F>
+  constexpr auto and_then(F&& f) const& {
+    using R = details::and_then_result_t<F, decltype(value())>;
+    if (has_value()) return std::invoke(std::forward<F>(f), value());
+    return R();
+  }
+
+  template <typename F>
+  constexpr auto and_then(F&& f) & {
+    using R = details::and_then_result_t<F, decltype(value())>;
+    if (has_value()) return std::invoke(std::forward<F>(f), value());
+    return R();
+  }
+
+  template <typename F>
+  constexpr auto and_then(F&& f) const&& {
+    using R = details::and_then_result_t<F, decltype(std::move(value()))>;
+    if (has_value()) return std::invoke(std::forward<F>(f), std::move(value()));
+    return R();
+  }
+
+  template <typename F>
+  constexpr auto and_then(F&& f) && {
+    using R = details::and_then_result_t<F, decltype(std::move(value()))>;
+    if (has_value()) return std::invoke(std::forward<F>(f), std::move(value()));
+    return R();
+  }
+};
+
+// Deduction guides.
+template <typename T>
+Optional(T) -> Optional<T>;
+
+template <typename T>
+Optional(std::optional<T>) -> Optional<T>;
+
+}  // namespace android::ftl
diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h
index 5217e76..49cde7f 100644
--- a/include/ftl/small_map.h
+++ b/include/ftl/small_map.h
@@ -17,11 +17,11 @@
 #pragma once
 
 #include <ftl/initializer_list.h>
+#include <ftl/optional.h>
 #include <ftl/small_vector.h>
 
 #include <algorithm>
 #include <functional>
-#include <optional>
 #include <type_traits>
 #include <utility>
 
@@ -47,7 +47,7 @@
 //   assert(!map.dynamic());
 //
 //   assert(map.contains(123));
-//   assert(map.get(42, [](const std::string& s) { return s.size(); }) == 3u);
+//   assert(map.get(42).transform([](const std::string& s) { return s.size(); }) == 3u);
 //
 //   const auto opt = map.get(-1);
 //   assert(opt);
@@ -59,7 +59,7 @@
 //   map.emplace_or_replace(0, "vanilla", 2u, 3u);
 //   assert(map.dynamic());
 //
-//   assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
+//   assert(map == SmallMap(ftl::init::map(-1, "xyz"sv)(0, "nil"sv)(42, "???"sv)(123, "abc"sv)));
 //
 template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>>
 class SmallMap final {
@@ -123,9 +123,7 @@
   const_iterator cend() const { return map_.cend(); }
 
   // Returns whether a mapping exists for the given key.
-  bool contains(const key_type& key) const {
-    return get(key, [](const mapped_type&) {});
-  }
+  bool contains(const key_type& key) const { return get(key).has_value(); }
 
   // Returns a reference to the value for the given key, or std::nullopt if the key was not found.
   //
@@ -139,44 +137,22 @@
   //   ref.get() = 'D';
   //   assert(d == 'D');
   //
-  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 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
-  // for the given key, or std::nullopt if the key was not found. If F has a return type of void,
-  // then the Boolean result indicates whether the key was found.
-  //
-  //   ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-  //
-  //   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 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) {
+  auto get(const key_type& key) const -> Optional<std::reference_wrapper<const mapped_type>> {
+    for (const auto& [k, v] : *this) {
       if (KeyEqual{}(k, key)) {
-        if constexpr (std::is_void_v<R>) {
-          f(v);
-          return true;
-        } else {
-          return f(v);
-        }
+        return std::cref(v);
       }
     }
-
     return {};
   }
 
-  template <typename F>
-  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)); });
+  auto get(const key_type& key) -> Optional<std::reference_wrapper<mapped_type>> {
+    for (auto& [k, v] : *this) {
+      if (KeyEqual{}(k, key)) {
+        return std::ref(v);
+      }
+    }
+    return {};
   }
 
   // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise.
@@ -286,7 +262,7 @@
 
   for (const auto& [k, v] : lhs) {
     const auto& lv = v;
-    if (!rhs.get(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
+    if (!rhs.get(k).transform([&lv](const W& rv) { return lv == rv; }).value_or(false)) {
       return false;
     }
   }
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
index 339726e..11294c3 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -21,11 +21,12 @@
 
 #include <algorithm>
 #include <iterator>
-#include <type_traits>
 #include <utility>
 #include <variant>
 #include <vector>
 
+#include <ftl/details/type_traits.h>
+
 namespace android::ftl {
 
 template <typename>
@@ -80,10 +81,6 @@
   using Static = StaticVector<T, N>;
   using Dynamic = SmallVector<T, 0>;
 
-  // TODO: Replace with std::remove_cvref_t in C++20.
-  template <typename U>
-  using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
-
  public:
   FTL_ARRAY_TRAIT(T, value_type);
   FTL_ARRAY_TRAIT(T, size_type);
@@ -104,7 +101,7 @@
 
   // Constructs at most N elements. See StaticVector for underlying constructors.
   template <typename Arg, typename... Args,
-            typename = std::enable_if_t<!is_small_vector<remove_cvref_t<Arg>>{}>>
+            typename = std::enable_if_t<!is_small_vector<details::remove_cvref_t<Arg>>{}>>
   SmallVector(Arg&& arg, Args&&... args)
       : vector_(std::in_place_type<Static>, std::forward<Arg>(arg), std::forward<Args>(args)...) {}
 
diff --git a/include/ftl/unit.h b/include/ftl/unit.h
new file mode 100644
index 0000000..e38230b
--- /dev/null
+++ b/include/ftl/unit.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// The unit type, and its only value.
+constexpr struct Unit {
+} unit;
+
+constexpr bool operator==(Unit, Unit) {
+  return true;
+}
+
+constexpr bool operator!=(Unit, Unit) {
+  return false;
+}
+
+// Adapts a function object F to return Unit. The return value of F is ignored.
+//
+// As a practical use, the function passed to ftl::Optional<T>::transform is not allowed to return
+// void (cf. https://wg21.link/P0798R8#mapping-functions-returning-void), but may return Unit if
+// only its side effects are meaningful:
+//
+//   ftl::Optional opt = "food"s;
+//   opt.transform(ftl::unit_fn([](std::string& str) { str.pop_back(); }));
+//   assert(opt == "foo"s);
+//
+template <typename F>
+struct UnitFn {
+  F f;
+
+  template <typename... Args>
+  Unit operator()(Args&&... args) {
+    return f(std::forward<Args>(args)...), unit;
+  }
+};
+
+template <typename F>
+constexpr auto unit_fn(F&& f) -> UnitFn<std::decay_t<F>> {
+  return {std::forward<F>(f)};
+}
+
+}  // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 9148fee..98a18c9 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
-#define _LIBINPUT_DISPLAY_VIEWPORT_H
+#pragma once
 
 #include <android-base/stringprintf.h>
 #include <ftl/enum.h>
@@ -144,5 +143,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_DISPLAY_VIEWPORT_H
diff --git a/include/input/Input.h b/include/input/Input.h
index a3c9f33..172e5b4 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_INPUT_H
-#define _LIBINPUT_INPUT_H
+#pragma once
 
 #pragma GCC system_header
 
@@ -290,6 +289,10 @@
      * The current gesture likely represents a user intentionally exerting force on the touchscreen.
      */
     DEEP_PRESS = AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS,
+    /**
+     * The current gesture represents the user swiping with two fingers on a touchpad.
+     */
+    TWO_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE,
 };
 
 /**
@@ -1104,5 +1107,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_INPUT_H
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 3585392..f4a1523 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_INPUT_DEVICE_H
-#define _LIBINPUT_INPUT_DEVICE_H
+#pragma once
 
 #include <android/sensor.h>
+#include <ftl/flags.h>
 #include <input/Input.h>
 #include <input/KeyCharacterMap.h>
 #include <unordered_map>
 #include <vector>
 
+#include "android/hardware/input/InputDeviceCountryCode.h"
+
 namespace android {
 
 /*
@@ -104,12 +106,18 @@
 };
 
 enum class InputDeviceLightType : int32_t {
-    MONO = 0,
+    INPUT = 0,
     PLAYER_ID = 1,
-    RGB = 2,
-    MULTI_COLOR = 3,
+    KEYBOARD_BACKLIGHT = 2,
 
-    ftl_last = MULTI_COLOR
+    ftl_last = KEYBOARD_BACKLIGHT
+};
+
+enum class InputDeviceLightCapability : uint32_t {
+    /** Capability to change brightness of the light */
+    BRIGHTNESS = 0x00000001,
+    /** Capability to change color of the light */
+    RGB = 0x00000002,
 };
 
 struct InputDeviceSensorInfo {
@@ -170,14 +178,17 @@
 
 struct InputDeviceLightInfo {
     explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type,
+                                  ftl::Flags<InputDeviceLightCapability> capabilityFlags,
                                   int32_t ordinal)
-          : name(name), id(id), type(type), ordinal(ordinal) {}
+          : name(name), id(id), type(type), capabilityFlags(capabilityFlags), ordinal(ordinal) {}
     // Name string of the light.
     std::string name;
     // Light id
     int32_t id;
     // Type of the light.
     InputDeviceLightType type;
+    // Light capabilities.
+    ftl::Flags<InputDeviceLightCapability> capabilityFlags;
     // Ordinal of the light
     int32_t ordinal;
 };
@@ -210,8 +221,10 @@
     };
 
     void initialize(int32_t id, int32_t generation, int32_t controllerNumber,
-            const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal,
-            bool hasMic);
+                    const InputDeviceIdentifier& identifier, const std::string& alias,
+                    bool isExternal, bool hasMic,
+                    hardware::input::InputDeviceCountryCode countryCode =
+                            hardware::input::InputDeviceCountryCode::INVALID);
 
     inline int32_t getId() const { return mId; }
     inline int32_t getControllerNumber() const { return mControllerNumber; }
@@ -223,6 +236,7 @@
     }
     inline bool isExternal() const { return mIsExternal; }
     inline bool hasMic() const { return mHasMic; }
+    inline hardware::input::InputDeviceCountryCode getCountryCode() const { return mCountryCode; }
     inline uint32_t getSources() const { return mSources; }
 
     const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
@@ -266,6 +280,9 @@
 
     std::vector<InputDeviceLightInfo> getLights();
 
+    inline void setSupportsUsi(bool supportsUsi) { mSupportsUsi = supportsUsi; }
+    inline bool supportsUsi() const { return mSupportsUsi; }
+
 private:
     int32_t mId;
     int32_t mGeneration;
@@ -274,9 +291,13 @@
     std::string mAlias;
     bool mIsExternal;
     bool mHasMic;
+    hardware::input::InputDeviceCountryCode mCountryCode;
     uint32_t mSources;
     int32_t mKeyboardType;
     std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
+    // Whether this device supports the Universal Stylus Initiative (USI) protocol for styluses.
+    bool mSupportsUsi;
+
     bool mHasVibrator;
     bool mHasBattery;
     bool mHasButtonUnderPad;
@@ -334,5 +355,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_INPUT_DEVICE_H
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 2a742f9..b4374ac 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_INPUT_EVENT_LABELS_H
-#define _LIBINPUT_INPUT_EVENT_LABELS_H
+#pragma once
 
 #include <input/Input.h>
 #include <android/keycodes.h>
@@ -68,4 +67,3 @@
 };
 
 } // namespace android
-#endif // _LIBINPUT_INPUT_EVENT_LABELS_H
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index dbc7bfa..1c52792 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_INPUT_TRANSPORT_H
-#define _LIBINPUT_INPUT_TRANSPORT_H
+#pragma once
 
 #pragma GCC system_header
 
@@ -674,5 +673,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_INPUT_TRANSPORT_H
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 1c9a5ea..dc928b8 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_KEY_CHARACTER_MAP_H
-#define _LIBINPUT_KEY_CHARACTER_MAP_H
+#pragma once
 
 #include <stdint.h>
 #include <list>
@@ -270,5 +269,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_KEY_CHARACTER_MAP_H
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 1da78aa..e203d19 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_KEY_LAYOUT_MAP_H
-#define _LIBINPUT_KEY_LAYOUT_MAP_H
+#pragma once
 
 #include <android-base/result.h>
 #include <stdint.h>
@@ -78,7 +77,7 @@
     std::optional<AxisInfo> mapAxis(int32_t scanCode) const;
     const std::string getLoadFileName() const;
     // Return pair of sensor type and sensor data index, for the input device abs code
-    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode);
+    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode) const;
 
     virtual ~KeyLayoutMap();
 
@@ -131,5 +130,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_KEY_LAYOUT_MAP_H
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 9a3e15f..f7f960f 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_KEYBOARD_H
-#define _LIBINPUT_KEYBOARD_H
+#pragma once
 
 #include <input/Input.h>
 #include <input/InputDevice.h>
@@ -88,5 +87,3 @@
 extern bool isMetaKey(int32_t keyCode);
 
 } // namespace android
-
-#endif // _LIBINPUT_KEYBOARD_H
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 0a75278..55f730b 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <map>
+#include <optional>
 #include <set>
 #include <string>
 
@@ -28,6 +29,15 @@
 }
 
 /**
+ * Convert an optional type to string.
+ */
+template <typename T>
+std::string toString(const std::optional<T>& optional,
+                     std::string (*toString)(const T&) = constToString) {
+    return optional ? toString(*optional) : "<not set>";
+}
+
+/**
  * Convert a set of integral types to string.
  */
 template <typename T>
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
index b1e3f85..28e4816 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UTILS_PROPERTY_MAP_H
-#define _UTILS_PROPERTY_MAP_H
+#pragma once
 
 #include <android-base/result.h>
 #include <utils/Tokenizer.h>
@@ -98,5 +97,3 @@
 };
 
 } // namespace android
-
-#endif // _UTILS_PROPERTY_MAP_H
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index eda628e..a616a95 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_TOUCHVIDEOFRAME_H
-#define _LIBINPUT_TOUCHVIDEOFRAME_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/time.h>
@@ -75,5 +74,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_TOUCHVIDEOFRAME_H
diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h
index 1acc2ae..f72a1bd 100644
--- a/include/input/VelocityControl.h
+++ b/include/input/VelocityControl.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_VELOCITY_CONTROL_H
-#define _LIBINPUT_VELOCITY_CONTROL_H
+#pragma once
 
 #include <input/Input.h>
 #include <input/VelocityTracker.h>
@@ -98,10 +97,8 @@
     VelocityControlParameters mParameters;
 
     nsecs_t mLastMovementTime;
-    VelocityTracker::Position mRawPosition;
+    float mRawPositionX, mRawPositionY;
     VelocityTracker mVelocityTracker;
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_VELOCITY_CONTROL_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 886f1f7..294879e 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_VELOCITY_TRACKER_H
-#define _LIBINPUT_VELOCITY_TRACKER_H
+#pragma once
 
 #include <input/Input.h>
 #include <utils/BitSet.h>
 #include <utils/Timers.h>
+#include <map>
+#include <set>
 
 namespace android {
 
@@ -46,18 +47,14 @@
         MAX = LEGACY,
     };
 
-    struct Position {
-        float x, y;
-    };
-
     struct Estimator {
         static const size_t MAX_DEGREE = 4;
 
         // Estimator time base.
         nsecs_t time;
 
-        // Polynomial coefficients describing motion in X and Y.
-        float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
+        // Polynomial coefficients describing motion.
+        float coeff[MAX_DEGREE + 1];
 
         // Polynomial degree (number of coefficients), or zero if no information is
         // available.
@@ -71,14 +68,40 @@
             degree = 0;
             confidence = 0;
             for (size_t i = 0; i <= MAX_DEGREE; i++) {
-                xCoeff[i] = 0;
-                yCoeff[i] = 0;
+                coeff[i] = 0;
             }
         }
     };
 
-    // Creates a velocity tracker using the specified strategy.
+    /*
+     * Contains all available velocity data from a VelocityTracker.
+     */
+    struct ComputedVelocity {
+        inline std::optional<float> getVelocity(int32_t axis, uint32_t id) const {
+            const auto& axisVelocities = mVelocities.find(axis);
+            if (axisVelocities == mVelocities.end()) {
+                return {};
+            }
+
+            const auto& axisIdVelocity = axisVelocities->second.find(id);
+            if (axisIdVelocity == axisVelocities->second.end()) {
+                return {};
+            }
+
+            return axisIdVelocity->second;
+        }
+
+        inline void addVelocity(int32_t axis, uint32_t id, float velocity) {
+            mVelocities[axis][id] = velocity;
+        }
+
+    private:
+        std::map<int32_t /*axis*/, std::map<int32_t /*pointerId*/, float /*velocity*/>> mVelocities;
+    };
+
+    // Creates a velocity tracker using the specified strategy for each supported axis.
     // If strategy is not provided, uses the default strategy for the platform.
+    // TODO(b/32830165): support axis-specific strategies.
     VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
 
     ~VelocityTracker();
@@ -92,47 +115,57 @@
     void clearPointers(BitSet32 idBits);
 
     // Adds movement information for a set of pointers.
-    // The idBits bitfield specifies the pointer ids of the pointers whose positions
+    // The idBits bitfield specifies the pointer ids of the pointers whose data points
     // are included in the movement.
-    // The positions array contains position information for each pointer in order by
-    // increasing id.  Its size should be equal to the number of one bits in idBits.
-    void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions);
+    // The positions map contains a mapping of an axis to positions array.
+    // The positions arrays contain information for each pointer in order by increasing id.
+    // Each array's size should be equal to the number of one bits in idBits.
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::map<int32_t, std::vector<float>>& positions);
 
     // Adds movement information for all pointers in a MotionEvent, including historical samples.
     void addMovement(const MotionEvent* event);
 
-    // Gets the velocity of the specified pointer id in position units per second.
-    // Returns false and sets the velocity components to zero if there is
-    // insufficient movement information for the pointer.
-    bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
+    // Returns the velocity of the specified pointer id and axis in position units per second.
+    // Returns empty optional if there is insufficient movement information for the pointer, or if
+    // the given axis is not supported for velocity tracking.
+    std::optional<float> getVelocity(int32_t axis, uint32_t id) const;
 
-    // Gets an estimator for the recent movements of the specified pointer id.
+    // Returns a ComputedVelocity instance with all available velocity data, using the given units
+    // (reference: units == 1 means "per millisecond"), and clamping each velocity between
+    // [-maxVelocity, maxVelocity], inclusive.
+    ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity);
+
+    // Gets an estimator for the recent movements of the specified pointer id for the given axis.
     // Returns false and clears the estimator if there is no information available
     // about the pointer.
-    bool getEstimator(uint32_t id, Estimator* outEstimator) const;
+    bool getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const;
 
     // Gets the active pointer id, or -1 if none.
     inline int32_t getActivePointerId() const { return mActivePointerId; }
 
-    // Gets a bitset containing all pointer ids from the most recent movement.
-    inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
-
 private:
-    // The default velocity tracker strategy.
-    // Although other strategies are available for testing and comparison purposes,
-    // this is the strategy that applications will actually use.  Be very careful
-    // when adjusting the default strategy because it can dramatically affect
-    // (often in a bad way) the user experience.
-    static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
-
     nsecs_t mLastEventTime;
     BitSet32 mCurrentPointerIdBits;
     int32_t mActivePointerId;
-    std::unique_ptr<VelocityTrackerStrategy> mStrategy;
 
-    bool configureStrategy(const Strategy strategy);
+    // An override strategy passed in the constructor to be used for all axes.
+    // This strategy will apply to all axes, unless the default strategy is specified here.
+    // When default strategy is specified, then each axis will use a potentially different strategy
+    // based on a hardcoded mapping.
+    const Strategy mOverrideStrategy;
+    // Maps axes to their respective VelocityTrackerStrategy instances.
+    // Note that, only axes that have had MotionEvents (and not all supported axes) will be here.
+    std::map<int32_t /*axis*/, std::unique_ptr<VelocityTrackerStrategy>> mConfiguredStrategies;
 
-    static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
+    void configureStrategy(int32_t axis);
+
+    // Generates a VelocityTrackerStrategy instance for the given Strategy type.
+    // The `deltaValues` parameter indicates whether or not the created strategy should treat motion
+    // values as deltas (and not as absolute values). This the parameter is applicable only for
+    // strategies that support differential axes.
+    static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy,
+                                                                   bool deltaValues);
 };
 
 
@@ -146,10 +179,9 @@
 public:
     virtual ~VelocityTrackerStrategy() { }
 
-    virtual void clear() = 0;
     virtual void clearPointers(BitSet32 idBits) = 0;
     virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                             const std::vector<VelocityTracker::Position>& positions) = 0;
+                             const std::vector<float>& positions) = 0;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
 };
 
@@ -178,10 +210,9 @@
     LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE);
     virtual ~LeastSquaresVelocityTrackerStrategy();
 
-    virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<VelocityTracker::Position>& positions) override;
+                     const std::vector<float>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -196,11 +227,9 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        VelocityTracker::Position positions[MAX_POINTERS];
+        float positions[MAX_POINTERS];
 
-        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
-            return positions[idBits.getIndexOfBit(id)];
-        }
+        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
     };
 
     float chooseWeight(uint32_t index) const;
@@ -221,10 +250,9 @@
     IntegratingVelocityTrackerStrategy(uint32_t degree);
     ~IntegratingVelocityTrackerStrategy();
 
-    virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<VelocityTracker::Position>& positions) override;
+                     const std::vector<float>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -233,16 +261,15 @@
         nsecs_t updateTime;
         uint32_t degree;
 
-        float xpos, xvel, xaccel;
-        float ypos, yvel, yaccel;
+        float pos, vel, accel;
     };
 
     const uint32_t mDegree;
     BitSet32 mPointerIdBits;
     State mPointerState[MAX_POINTER_ID + 1];
 
-    void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
-    void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
+    void initState(State& state, nsecs_t eventTime, float pos) const;
+    void updateState(State& state, nsecs_t eventTime, float pos) const;
     void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
 };
 
@@ -255,10 +282,9 @@
     LegacyVelocityTrackerStrategy();
     virtual ~LegacyVelocityTrackerStrategy();
 
-    virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<VelocityTracker::Position>& positions) override;
+                     const std::vector<float>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -274,11 +300,9 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        VelocityTracker::Position positions[MAX_POINTERS];
+        float positions[MAX_POINTERS];
 
-        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
-            return positions[idBits.getIndexOfBit(id)];
-        }
+        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
     };
 
     uint32_t mIndex;
@@ -287,13 +311,12 @@
 
 class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy {
 public:
-    ImpulseVelocityTrackerStrategy();
+    ImpulseVelocityTrackerStrategy(bool deltaValues);
     virtual ~ImpulseVelocityTrackerStrategy();
 
-    virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<VelocityTracker::Position>& positions) override;
+                     const std::vector<float>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -308,17 +331,18 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        VelocityTracker::Position positions[MAX_POINTERS];
+        float positions[MAX_POINTERS];
 
-        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
-            return positions[idBits.getIndexOfBit(id)];
-        }
+        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
     };
 
+    // Whether or not the input movement values for the strategy come in the form of delta values.
+    // If the input values are not deltas, the strategy needs to calculate deltas as part of its
+    // velocity calculation.
+    const bool mDeltaValues;
+
     size_t mIndex;
     Movement mMovements[HISTORY_SIZE];
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_VELOCITY_TRACKER_H
diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h
index 6e8e2c9..a4381ea 100644
--- a/include/input/VirtualKeyMap.h
+++ b/include/input/VirtualKeyMap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LIBINPUT_VIRTUAL_KEY_MAP_H
-#define _LIBINPUT_VIRTUAL_KEY_MAP_H
+#pragma once
 
 #include <stdint.h>
 
@@ -77,5 +76,3 @@
 };
 
 } // namespace android
-
-#endif // _LIBINPUT_KEY_CHARACTER_MAP_H
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 15bd5c3..ebc74fb 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -23,8 +23,10 @@
 #include <sys/eventfd.h>
 #include <sys/uio.h>
 
+#include <atomic>
 #include <chrono>
 #include <deque>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <tuple>
diff --git a/libs/arect/include/android/rect.h b/libs/arect/include/android/rect.h
index b36728e..d52861a 100644
--- a/libs/arect/include/android/rect.h
+++ b/libs/arect/include/android/rect.h
@@ -57,7 +57,7 @@
 } ARect;
 
 #ifdef __cplusplus
-};
+}
 #endif
 
 #endif // ANDROID_RECT_H
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 5e9f540..c4bb6d0 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -71,9 +71,46 @@
 }
 
 cc_defaults {
-    name: "libbinder_defaults",
+    name: "libbinder_common_defaults",
     host_supported: true,
 
+    srcs: [
+        "Binder.cpp",
+        "BinderRecordReplay.cpp",
+        "BpBinder.cpp",
+        "Debug.cpp",
+        "FdTrigger.cpp",
+        "IInterface.cpp",
+        "IResultReceiver.cpp",
+        "Parcel.cpp",
+        "ParcelFileDescriptor.cpp",
+        "RpcSession.cpp",
+        "RpcServer.cpp",
+        "RpcState.cpp",
+        "Stability.cpp",
+        "Status.cpp",
+        "TextOutput.cpp",
+        "Trace.cpp",
+        "Utils.cpp",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libbase",
+    ],
+
+    header_libs: [
+        "libbinder_headers",
+    ],
+}
+
+cc_defaults {
+    name: "libbinder_android_defaults",
+
     // TODO(b/31559095): get headers from bionic on host
     include_dirs: [
         "bionic/libc/kernel/android/uapi/",
@@ -81,23 +118,8 @@
     ],
 
     srcs: [
-        "Binder.cpp",
-        "BpBinder.cpp",
-        "Debug.cpp",
-        "FdTrigger.cpp",
-        "IInterface.cpp",
-        "IResultReceiver.cpp",
         "OS.cpp",
-        "Parcel.cpp",
-        "ParcelFileDescriptor.cpp",
-        "RpcSession.cpp",
-        "RpcServer.cpp",
-        "RpcState.cpp",
         "RpcTransportRaw.cpp",
-        "Stability.cpp",
-        "Status.cpp",
-        "TextOutput.cpp",
-        "Utils.cpp",
     ],
 
     target: {
@@ -127,22 +149,18 @@
         },
 
         debuggable: {
-            cflags: ["-DBINDER_RPC_DEV_SERVERS"],
+            cflags: [
+                "-DBINDER_RPC_DEV_SERVERS",
+                "-DBINDER_ENABLE_RECORDING",
+            ],
         },
     },
 
     shared_libs: [
         "liblog",
-        "libcutils",
-        "libutils",
-    ],
-
-    static_libs: [
-        "libbase",
     ],
 
     header_libs: [
-        "libbinder_headers",
         "libandroid_runtime_vm_headers",
     ],
 
@@ -177,6 +195,48 @@
     ],
 }
 
+cc_library_shared {
+    name: "libbinder_on_trusty_mock",
+    defaults: ["libbinder_common_defaults"],
+
+    srcs: [
+        // Trusty-specific files
+        "trusty/logging.cpp",
+        "trusty/OS.cpp",
+        "trusty/RpcServerTrusty.cpp",
+        "trusty/RpcTransportTipcTrusty.cpp",
+        "trusty/TrustyStatus.cpp",
+        "trusty/socket.cpp",
+    ],
+
+    cflags: [
+        "-DBINDER_RPC_SINGLE_THREADED",
+        // Trusty libbinder uses vendor stability for its binders
+        "-D__ANDROID_VNDK__",
+        "-U__ANDROID__",
+        "-D__TRUSTY__",
+        "-DTRUSTY_USERSPACE",
+        // Flags from the Trusty build system
+        "-Werror",
+        "-Wsign-compare",
+        "-Wno-unused-function",
+        "-Wno-unused-label",
+        "-fno-common",
+        "-fno-omit-frame-pointer",
+        "-fno-threadsafe-statics",
+    ],
+    rtti: false,
+
+    local_include_dirs: [
+        "trusty/include",
+        "trusty/include_mock",
+    ],
+
+    visibility: [
+        ":__subpackages__",
+    ],
+}
+
 cc_defaults {
     name: "libbinder_kernel_defaults",
     srcs: [
@@ -208,7 +268,8 @@
 cc_library {
     name: "libbinder",
     defaults: [
-        "libbinder_defaults",
+        "libbinder_common_defaults",
+        "libbinder_android_defaults",
         "libbinder_kernel_defaults",
     ],
 
@@ -264,7 +325,10 @@
 
 cc_library_static {
     name: "libbinder_rpc_no_kernel",
-    defaults: ["libbinder_defaults"],
+    defaults: [
+        "libbinder_common_defaults",
+        "libbinder_android_defaults",
+    ],
     visibility: [
         ":__subpackages__",
     ],
@@ -273,7 +337,8 @@
 cc_library_static {
     name: "libbinder_rpc_single_threaded",
     defaults: [
-        "libbinder_defaults",
+        "libbinder_common_defaults",
+        "libbinder_android_defaults",
         "libbinder_kernel_defaults",
     ],
     cflags: [
@@ -286,7 +351,10 @@
 
 cc_library_static {
     name: "libbinder_rpc_single_threaded_no_kernel",
-    defaults: ["libbinder_defaults"],
+    defaults: [
+        "libbinder_common_defaults",
+        "libbinder_android_defaults",
+    ],
     cflags: [
         "-DBINDER_RPC_SINGLE_THREADED",
     ],
@@ -335,6 +403,34 @@
     defaults: ["libbinder_tls_defaults"],
 }
 
+cc_library_shared {
+    name: "libbinder_trusty",
+    vendor: true,
+    srcs: [
+        "RpcTransportTipcAndroid.cpp",
+        "RpcTrusty.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libtrusty",
+        "libutils",
+    ],
+    static_libs: [
+        "libbase",
+    ],
+    export_include_dirs: ["include_trusty"],
+
+    // Most of Android doesn't need this library and shouldn't use it,
+    // so we restrict its visibility to the Trusty-specific packages.
+    visibility: [
+        ":__subpackages__",
+        "//system/core/trusty:__subpackages__",
+        "//vendor:__subpackages__",
+    ],
+}
+
 // For testing
 cc_library_static {
     name: "libbinder_tls_static",
@@ -388,6 +484,9 @@
         java: {
             enabled: false,
         },
+        cpp: {
+            gen_trace: false,
+        },
     },
 }
 
@@ -412,6 +511,7 @@
     // This library is intentionally limited to these targets, and it will be removed later.
     // Do not expand the visibility.
     visibility: [
+        ":__subpackages__",
         "//packages/modules/Virtualization:__subpackages__",
     ],
 }
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index b5ea60f..5e725a9 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -21,6 +21,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
+#include <binder/BinderRecordReplay.h>
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
 #include <binder/IPCThreadState.h>
@@ -28,7 +29,9 @@
 #include <binder/IShellCallback.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
+#include <cutils/compiler.h>
 #include <private/android_filesystem_config.h>
+#include <pthread.h>
 #include <utils/misc.h>
 
 #include <inttypes.h>
@@ -60,6 +63,12 @@
 bool kEnableRpcDevServers = false;
 #endif
 
+#ifdef BINDER_ENABLE_RECORDING
+bool kEnableRecording = true;
+#else
+bool kEnableRecording = false;
+#endif
+
 // Log any reply transactions for which the data exceeds this size
 #define LOG_REPLIES_OVER_SIZE (300 * 1024)
 // ---------------------------------------------------------------------------
@@ -202,6 +211,17 @@
     proxy->withLock(doWithLock);
 }
 
+sp<IBinder> IBinder::lookupOrCreateWeak(const void* objectID, object_make_func make,
+                                        const void* makeArgs) {
+    BBinder* local = localBinder();
+    if (local) {
+        return local->lookupOrCreateWeak(objectID, make, makeArgs);
+    }
+    BpBinder* proxy = this->remoteBinder();
+    LOG_ALWAYS_FATAL_IF(proxy == nullptr, "binder object must be either local or remote");
+    return proxy->lookupOrCreateWeak(objectID, make, makeArgs);
+}
+
 // ---------------------------------------------------------------------------
 
 class BBinder::RpcServerLink : public IBinder::DeathRecipient {
@@ -212,7 +232,10 @@
           : mRpcServer(rpcServer), mKeepAliveBinder(keepAliveBinder), mBinder(binder) {}
     virtual ~RpcServerLink();
     void binderDied(const wp<IBinder>&) override {
-        LOG_RPC_DETAIL("RpcServerLink: binder died, shutting down RpcServer");
+        auto promoted = mBinder.promote();
+        ALOGI("RpcBinder: binder died, shutting down RpcServer for %s",
+              promoted ? String8(promoted->getInterfaceDescriptor()).c_str() : "<NULL>");
+
         if (mRpcServer == nullptr) {
             ALOGW("RpcServerLink: Unable to shut down RpcServer because it does not exist.");
         } else {
@@ -221,11 +244,7 @@
         }
         mRpcServer.clear();
 
-        auto promoted = mBinder.promote();
-        if (promoted == nullptr) {
-            ALOGW("RpcServerLink: Unable to remove link from parent binder object because parent "
-                  "binder object is gone.");
-        } else {
+        if (promoted) {
             promoted->removeRpcServerLink(sp<RpcServerLink>::fromExisting(this));
         }
         mBinder.clear();
@@ -254,11 +273,13 @@
     Mutex mLock;
     std::set<sp<RpcServerLink>> mRpcServerLinks;
     BpBinder::ObjectManager mObjects;
+
+    android::base::unique_fd mRecordingFd;
 };
 
 // ---------------------------------------------------------------------------
 
-BBinder::BBinder() : mExtras(nullptr), mStability(0), mParceled(false) {}
+BBinder::BBinder() : mExtras(nullptr), mStability(0), mParceled(false), mRecordingOn(false) {}
 
 bool BBinder::isBinderAlive() const
 {
@@ -270,13 +291,68 @@
     return NO_ERROR;
 }
 
+status_t BBinder::startRecordingTransactions(const Parcel& data) {
+    if (!kEnableRecording) {
+        ALOGW("Binder recording disallowed because recording is not enabled");
+        return INVALID_OPERATION;
+    }
+    if (!kEnableKernelIpc) {
+        ALOGW("Binder recording disallowed because kernel binder is not enabled");
+        return INVALID_OPERATION;
+    }
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (uid != AID_ROOT) {
+        ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid);
+        return PERMISSION_DENIED;
+    }
+    Extras* e = getOrCreateExtras();
+    AutoMutex lock(e->mLock);
+    if (mRecordingOn) {
+        LOG(INFO) << "Could not start Binder recording. Another is already in progress.";
+        return INVALID_OPERATION;
+    } else {
+        status_t readStatus = data.readUniqueFileDescriptor(&(e->mRecordingFd));
+        if (readStatus != OK) {
+            return readStatus;
+        }
+        mRecordingOn = true;
+        LOG(INFO) << "Started Binder recording.";
+        return NO_ERROR;
+    }
+}
+
+status_t BBinder::stopRecordingTransactions() {
+    if (!kEnableRecording) {
+        ALOGW("Binder recording disallowed because recording is not enabled");
+        return INVALID_OPERATION;
+    }
+    if (!kEnableKernelIpc) {
+        ALOGW("Binder recording disallowed because kernel binder is not enabled");
+        return INVALID_OPERATION;
+    }
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (uid != AID_ROOT) {
+        ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid);
+        return PERMISSION_DENIED;
+    }
+    Extras* e = getOrCreateExtras();
+    AutoMutex lock(e->mLock);
+    if (mRecordingOn) {
+        e->mRecordingFd.reset();
+        mRecordingOn = false;
+        LOG(INFO) << "Stopped Binder recording.";
+        return NO_ERROR;
+    } else {
+        LOG(INFO) << "Could not stop Binder recording. One is not in progress.";
+        return INVALID_OPERATION;
+    }
+}
+
 const String16& BBinder::getInterfaceDescriptor() const
 {
-    // This is a local static rather than a global static,
-    // to avoid static initializer ordering issues.
-    static String16 sEmptyDescriptor;
-    ALOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this);
-    return sEmptyDescriptor;
+    static StaticString16 sBBinder(u"BBinder");
+    ALOGW("Reached BBinder::getInterfaceDescriptor (this=%p). Override?", this);
+    return sBBinder;
 }
 
 // NOLINTNEXTLINE(google-default-arguments)
@@ -294,6 +370,12 @@
         case PING_TRANSACTION:
             err = pingBinder();
             break;
+        case START_RECORDING_TRANSACTION:
+            err = startRecordingTransactions(data);
+            break;
+        case STOP_RECORDING_TRANSACTION:
+            err = stopRecordingTransactions();
+            break;
         case EXTENSION_TRANSACTION:
             CHECK(reply != nullptr);
             err = reply->writeStrongBinder(getExtension());
@@ -320,6 +402,26 @@
         }
     }
 
+    if (CC_UNLIKELY(kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION)) {
+        Extras* e = mExtras.load(std::memory_order_acquire);
+        AutoMutex lock(e->mLock);
+        if (mRecordingOn) {
+            Parcel emptyReply;
+            auto transaction =
+                    android::binder::debug::RecordedTransaction::fromDetails(code, flags, data,
+                                                                             reply ? *reply
+                                                                                   : emptyReply,
+                                                                             err);
+            if (transaction) {
+                if (status_t err = transaction->dumpToFile(e->mRecordingFd); err != NO_ERROR) {
+                    LOG(INFO) << "Failed to dump RecordedTransaction to file with error " << err;
+                }
+            } else {
+                LOG(INFO) << "Failed to create RecordedTransaction object.";
+            }
+        }
+    }
+
     return err;
 }
 
@@ -378,6 +480,14 @@
     doWithLock();
 }
 
+sp<IBinder> BBinder::lookupOrCreateWeak(const void* objectID, object_make_func make,
+                                        const void* makeArgs) {
+    Extras* e = getOrCreateExtras();
+    LOG_ALWAYS_FATAL_IF(!e, "no memory");
+    AutoMutex _l(e->mLock);
+    return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
+}
+
 BBinder* BBinder::localBinder()
 {
     return this;
@@ -595,6 +705,7 @@
         return status;
     }
     rpcServer->setMaxThreads(binderThreadPoolMaxCount);
+    LOG(INFO) << "RpcBinder: Started Binder debug on " << getInterfaceDescriptor();
     rpcServer->start();
     e->mRpcServerLinks.emplace(link);
     LOG_RPC_DETAIL("%s(fd=%d) successful", __PRETTY_FUNCTION__, socketFdForPrint);
diff --git a/libs/binder/BinderRecordReplay.cpp b/libs/binder/BinderRecordReplay.cpp
new file mode 100644
index 0000000..90c02a8
--- /dev/null
+++ b/libs/binder/BinderRecordReplay.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <binder/BinderRecordReplay.h>
+#include <algorithm>
+
+using android::Parcel;
+using android::base::unique_fd;
+using android::binder::debug::RecordedTransaction;
+
+#define PADDING8(s) ((8 - (s) % 8) % 8)
+
+static_assert(PADDING8(0) == 0);
+static_assert(PADDING8(1) == 7);
+static_assert(PADDING8(7) == 1);
+static_assert(PADDING8(8) == 0);
+
+// Transactions are sequentially recorded to the file descriptor in the following format:
+//
+// RecordedTransaction.TransactionHeader  (32 bytes)
+// Sent Parcel data                       (getDataSize() bytes)
+// padding                                (enough bytes to align the reply Parcel data to 8 bytes)
+// Reply Parcel data                      (getReplySize() bytes)
+// padding                                (enough bytes to align the next header to 8 bytes)
+// [repeats with next transaction]
+//
+// Warning: This format is non-stable
+
+RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept {
+    mHeader = {t.getCode(),      t.getFlags(),          t.getDataSize(),
+               t.getReplySize(), t.getReturnedStatus(), t.getVersion()};
+    mSent.setData(t.getDataParcel().data(), t.getDataSize());
+    mReply.setData(t.getReplyParcel().data(), t.getReplySize());
+}
+
+std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t code, uint32_t flags,
+                                                                    const Parcel& dataParcel,
+                                                                    const Parcel& replyParcel,
+                                                                    status_t err) {
+    RecordedTransaction t;
+    t.mHeader = {code,
+                 flags,
+                 static_cast<uint64_t>(dataParcel.dataSize()),
+                 static_cast<uint64_t>(replyParcel.dataSize()),
+                 static_cast<int32_t>(err),
+                 dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)};
+
+    if (t.mSent.setData(dataParcel.data(), t.getDataSize()) != android::NO_ERROR) {
+        LOG(INFO) << "Failed to set sent parcel data.";
+        return std::nullopt;
+    }
+
+    if (t.mReply.setData(replyParcel.data(), t.getReplySize()) != android::NO_ERROR) {
+        LOG(INFO) << "Failed to set reply parcel data.";
+        return std::nullopt;
+    }
+
+    return std::optional<RecordedTransaction>(std::move(t));
+}
+
+std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) {
+    RecordedTransaction t;
+    if (!android::base::ReadFully(fd, &t.mHeader, sizeof(mHeader))) {
+        LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get();
+        return std::nullopt;
+    }
+    if (t.getVersion() != 0) {
+        LOG(INFO) << "File corrupted: transaction version is not 0.";
+        return std::nullopt;
+    }
+
+    std::vector<uint8_t> bytes;
+    bytes.resize(t.getDataSize());
+    if (!android::base::ReadFully(fd, bytes.data(), t.getDataSize())) {
+        LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get();
+        return std::nullopt;
+    }
+    if (t.mSent.setData(bytes.data(), t.getDataSize()) != android::NO_ERROR) {
+        LOG(INFO) << "Failed to set sent parcel data.";
+        return std::nullopt;
+    }
+
+    uint8_t padding[7];
+    if (!android::base::ReadFully(fd, padding, PADDING8(t.getDataSize()))) {
+        LOG(INFO) << "Failed to read sent parcel padding from fd " << fd.get();
+        return std::nullopt;
+    }
+    if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) {
+        LOG(INFO) << "File corrupted: padding isn't 0.";
+        return std::nullopt;
+    }
+
+    bytes.resize(t.getReplySize());
+    if (!android::base::ReadFully(fd, bytes.data(), t.getReplySize())) {
+        LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get();
+        return std::nullopt;
+    }
+    if (t.mReply.setData(bytes.data(), t.getReplySize()) != android::NO_ERROR) {
+        LOG(INFO) << "Failed to set reply parcel data.";
+        return std::nullopt;
+    }
+
+    if (!android::base::ReadFully(fd, padding, PADDING8(t.getReplySize()))) {
+        LOG(INFO) << "Failed to read parcel padding from fd " << fd.get();
+        return std::nullopt;
+    }
+    if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) {
+        LOG(INFO) << "File corrupted: padding isn't 0.";
+        return std::nullopt;
+    }
+
+    return std::optional<RecordedTransaction>(std::move(t));
+}
+
+android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const {
+    if (!android::base::WriteFully(fd, &mHeader, sizeof(mHeader))) {
+        LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    if (!android::base::WriteFully(fd, mSent.data(), getDataSize())) {
+        LOG(INFO) << "Failed to write sent parcel data to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    const uint8_t zeros[7] = {0};
+    if (!android::base::WriteFully(fd, zeros, PADDING8(getDataSize()))) {
+        LOG(INFO) << "Failed to write sent parcel padding to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    if (!android::base::WriteFully(fd, mReply.data(), getReplySize())) {
+        LOG(INFO) << "Failed to write reply parcel data to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    if (!android::base::WriteFully(fd, zeros, PADDING8(getReplySize()))) {
+        LOG(INFO) << "Failed to write reply parcel padding to fd " << fd.get();
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
+uint32_t RecordedTransaction::getCode() const {
+    return mHeader.code;
+}
+
+uint32_t RecordedTransaction::getFlags() const {
+    return mHeader.flags;
+}
+
+uint64_t RecordedTransaction::getDataSize() const {
+    return mHeader.dataSize;
+}
+
+uint64_t RecordedTransaction::getReplySize() const {
+    return mHeader.replySize;
+}
+
+int32_t RecordedTransaction::getReturnedStatus() const {
+    return mHeader.statusReturned;
+}
+
+uint32_t RecordedTransaction::getVersion() const {
+    return mHeader.version;
+}
+
+const Parcel& RecordedTransaction::getDataParcel() const {
+    return mSent;
+}
+
+const Parcel& RecordedTransaction::getReplyParcel() const {
+    return mReply;
+}
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index b6d35ef..54d2445 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -30,6 +30,8 @@
 
 #include "BuildFlags.h"
 
+#include <android-base/file.h>
+
 //#undef ALOGV
 //#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
 
@@ -100,6 +102,36 @@
     return value;
 }
 
+namespace {
+struct Tag {
+    wp<IBinder> binder;
+};
+} // namespace
+
+static void cleanWeak(const void* /* id */, void* obj, void* /* cookie */) {
+    delete static_cast<Tag*>(obj);
+}
+
+sp<IBinder> BpBinder::ObjectManager::lookupOrCreateWeak(const void* objectID, object_make_func make,
+                                                        const void* makeArgs) {
+    entry_t& e = mObjects[objectID];
+    if (e.object != nullptr) {
+        if (auto attached = static_cast<Tag*>(e.object)->binder.promote()) {
+            return attached;
+        }
+    } else {
+        e.object = new Tag;
+        LOG_ALWAYS_FATAL_IF(!e.object, "no more memory");
+    }
+    sp<IBinder> newObj = make(makeArgs);
+
+    static_cast<Tag*>(e.object)->binder = newObj;
+    e.cleanupCookie = nullptr;
+    e.func = cleanWeak;
+
+    return newObj;
+}
+
 void BpBinder::ObjectManager::kill()
 {
     const size_t N = mObjects.size();
@@ -269,6 +301,18 @@
     return transact(PING_TRANSACTION, data, &reply);
 }
 
+status_t BpBinder::startRecordingBinder(const android::base::unique_fd& fd) {
+    Parcel send, reply;
+    send.writeUniqueFileDescriptor(fd);
+    return transact(START_RECORDING_TRANSACTION, send, &reply);
+}
+
+status_t BpBinder::stopRecordingBinder() {
+    Parcel data, reply;
+    data.markForBinder(sp<BpBinder>::fromExisting(this));
+    return transact(STOP_RECORDING_TRANSACTION, data, &reply);
+}
+
 status_t BpBinder::dump(int fd, const Vector<String16>& args)
 {
     Parcel send;
@@ -516,6 +560,12 @@
     doWithLock();
 }
 
+sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make,
+                                         const void* makeArgs) {
+    AutoMutex _l(mLock);
+    return mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
+}
+
 BpBinder* BpBinder::remoteBinder()
 {
     return this;
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index d123fd1..8ee6cb0 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -22,6 +22,7 @@
 #include <poll.h>
 
 #include <android-base/macros.h>
+#include <android-base/scopeguard.h>
 
 #include "RpcState.h"
 namespace android {
@@ -53,25 +54,34 @@
 #endif
 }
 
-status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) {
+status_t FdTrigger::triggerablePoll(const android::RpcTransportFd& transportFd, int16_t event) {
 #ifdef BINDER_RPC_SINGLE_THREADED
     if (mTriggered) {
         return DEAD_OBJECT;
     }
 #endif
 
-    LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", fd.get());
+    LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed",
+                        transportFd.fd.get());
     pollfd pfd[]{
-            {.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
+            {.fd = transportFd.fd.get(), .events = static_cast<int16_t>(event), .revents = 0},
 #ifndef BINDER_RPC_SINGLE_THREADED
             {.fd = mRead.get(), .events = 0, .revents = 0},
 #endif
     };
+
+    LOG_ALWAYS_FATAL_IF(transportFd.isInPollingState() == true,
+                        "Only one thread should be polling on Fd!");
+
+    transportFd.setPollingState(true);
+    auto pollingStateGuard =
+            android::base::make_scope_guard([&]() { transportFd.setPollingState(false); });
+
     int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
     if (ret < 0) {
         return -errno;
     }
-    LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", fd.get());
+    LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", transportFd.fd.get());
 
     // At least one FD has events. Check them.
 
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index a25dc11..5fbf290 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -21,6 +21,8 @@
 #include <android-base/unique_fd.h>
 #include <utils/Errors.h>
 
+#include <binder/RpcTransport.h>
+
 namespace android {
 
 /** This is not a pipe. */
@@ -53,7 +55,8 @@
      *   true - time to read!
      *   false - trigger happened
      */
-    [[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
+    [[nodiscard]] status_t triggerablePoll(const android::RpcTransportFd& transportFd,
+                                           int16_t event);
 
 private:
 #ifdef BINDER_RPC_SINGLE_THREADED
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index b50cfb3..11c8e5d 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -39,7 +39,6 @@
 #include <sys/resource.h>
 #include <unistd.h>
 
-#include "Static.h"
 #include "binder_module.h"
 
 #if LOG_NDEBUG
@@ -124,46 +123,43 @@
         return "unknown";
 }
 
-static const void* printBinderTransactionData(TextOutput& out, const void* data)
-{
+static const void* printBinderTransactionData(std::ostream& out, const void* data) {
     const binder_transaction_data* btd =
         (const binder_transaction_data*)data;
     if (btd->target.handle < 1024) {
         /* want to print descriptors in decimal; guess based on value */
-        out << "target.desc=" << btd->target.handle;
+        out << "\ttarget.desc=" << btd->target.handle;
     } else {
-        out << "target.ptr=" << btd->target.ptr;
+        out << "\ttarget.ptr=" << btd->target.ptr;
     }
-    out << " (cookie " << btd->cookie << ")" << endl
-        << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << endl
-        << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
-        << " bytes)" << endl
-        << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
-        << " bytes)";
+    out << "\t (cookie " << btd->cookie << ")"
+        << "\n"
+        << "\tcode=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << "\n"
+        << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)"
+        << "\n"
+        << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)";
     return btd+1;
 }
 
-static const void* printReturnCommand(TextOutput& out, const void* _cmd)
-{
+static const void* printReturnCommand(std::ostream& out, const void* _cmd) {
     static const size_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
     const int32_t* cmd = (const int32_t*)_cmd;
     uint32_t code = (uint32_t)*cmd++;
     size_t cmdIndex = code & 0xff;
     if (code == BR_ERROR) {
-        out << "BR_ERROR: " << (void*)(uint64_t)(*cmd++) << endl;
+        out << "\tBR_ERROR: " << (void*)(uint64_t)(*cmd++) << "\n";
         return cmd;
     } else if (cmdIndex >= N) {
-        out << "Unknown reply: " << code << endl;
+        out << "\tUnknown reply: " << code << "\n";
         return cmd;
     }
-    out << kReturnStrings[cmdIndex];
+    out << "\t" << kReturnStrings[cmdIndex];
 
     switch (code) {
         case BR_TRANSACTION:
         case BR_REPLY: {
-            out << ": " << indent;
-            cmd = (const int32_t *)printBinderTransactionData(out, cmd);
-            out << dedent;
+            out << ": ";
+            cmd = (const int32_t*)printBinderTransactionData(out, cmd);
         } break;
 
         case BR_ACQUIRE_RESULT: {
@@ -200,19 +196,18 @@
             break;
     }
 
-    out << endl;
+    out << "\n";
     return cmd;
 }
 
-static const void* printCommand(TextOutput& out, const void* _cmd)
-{
+static const void* printCommand(std::ostream& out, const void* _cmd) {
     static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
     const int32_t* cmd = (const int32_t*)_cmd;
     uint32_t code = (uint32_t)*cmd++;
     size_t cmdIndex = code & 0xff;
 
     if (cmdIndex >= N) {
-        out << "Unknown command: " << code << endl;
+        out << "Unknown command: " << code << "\n";
         return cmd;
     }
     out << kCommandStrings[cmdIndex];
@@ -220,9 +215,8 @@
     switch (code) {
         case BC_TRANSACTION:
         case BC_REPLY: {
-            out << ": " << indent;
-            cmd = (const int32_t *)printBinderTransactionData(out, cmd);
-            out << dedent;
+            out << ": ";
+            cmd = (const int32_t*)printBinderTransactionData(out, cmd);
         } break;
 
         case BC_ACQUIRE_RESULT: {
@@ -274,7 +268,7 @@
             break;
     }
 
-    out << endl;
+    out << "\n";
     return cmd;
 }
 
@@ -548,8 +542,10 @@
         if (IN < sizeof(int32_t)) return result;
         cmd = mIn.readInt32();
         IF_LOG_COMMANDS() {
-            alog << "Processing top-level Command: "
-                 << getReturnString(cmd) << endl;
+            std::ostringstream logStream;
+            logStream << "Processing top-level Command: " << getReturnString(cmd) << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
 
         pthread_mutex_lock(&mProcess->mThreadCountLock);
@@ -726,10 +722,11 @@
     flags |= TF_ACCEPT_FDS;
 
     IF_LOG_TRANSACTIONS() {
-        TextOutput::Bundle _b(alog);
-        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
-            << handle << " / code " << TypeCode(code) << ": "
-            << indent << data << dedent << endl;
+        std::ostringstream logStream;
+        logStream << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " << handle
+                  << " / code " << TypeCode(code) << ": \t" << data << "\n";
+        std::string message = logStream.str();
+        ALOGI("%s", message.c_str());
     }
 
     LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
@@ -774,11 +771,15 @@
         #endif
 
         IF_LOG_TRANSACTIONS() {
-            TextOutput::Bundle _b(alog);
-            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
-                << handle << ": ";
-            if (reply) alog << indent << *reply << dedent << endl;
-            else alog << "(none requested)" << endl;
+            std::ostringstream logStream;
+            logStream << "BR_REPLY thr " << (void*)pthread_self() << " / hand " << handle << ": ";
+            if (reply)
+                logStream << "\t" << *reply << "\n";
+            else
+                logStream << "(none requested)"
+                          << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
     } else {
         err = waitForResponse(nullptr, nullptr);
@@ -920,8 +921,10 @@
         cmd = (uint32_t)mIn.readInt32();
 
         IF_LOG_COMMANDS() {
-            alog << "Processing waitForResponse Command: "
-                << getReturnString(cmd) << endl;
+            std::ostringstream logStream;
+            logStream << "Processing waitForResponse Command: " << getReturnString(cmd) << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
 
         switch (cmd) {
@@ -1033,17 +1036,19 @@
     }
 
     IF_LOG_COMMANDS() {
-        TextOutput::Bundle _b(alog);
+        std::ostringstream logStream;
         if (outAvail != 0) {
-            alog << "Sending commands to driver: " << indent;
+            logStream << "Sending commands to driver: ";
             const void* cmds = (const void*)bwr.write_buffer;
-            const void* end = ((const uint8_t*)cmds)+bwr.write_size;
-            alog << HexDump(cmds, bwr.write_size) << endl;
-            while (cmds < end) cmds = printCommand(alog, cmds);
-            alog << dedent;
+            const void* end = ((const uint8_t*)cmds) + bwr.write_size;
+            logStream << "\t" << HexDump(cmds, bwr.write_size) << "\n";
+            while (cmds < end) cmds = printCommand(logStream, cmds);
         }
-        alog << "Size of receive buffer: " << bwr.read_size
-            << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
+        logStream << "Size of receive buffer: " << bwr.read_size << ", needRead: " << needRead
+                  << ", doReceive: " << doReceive << "\n";
+
+        std::string message = logStream.str();
+        ALOGI("%s", message.c_str());
     }
 
     // Return immediately if there is nothing to do.
@@ -1054,7 +1059,10 @@
     status_t err;
     do {
         IF_LOG_COMMANDS() {
-            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
+            std::ostringstream logStream;
+            logStream << "About to read/write, write size = " << mOut.dataSize() << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
 #if defined(__ANDROID__)
         if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
@@ -1068,14 +1076,20 @@
             err = -EBADF;
         }
         IF_LOG_COMMANDS() {
-            alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
+            std::ostringstream logStream;
+            logStream << "Finished read/write, write size = " << mOut.dataSize() << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
     } while (err == -EINTR);
 
     IF_LOG_COMMANDS() {
-        alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "
-            << bwr.write_consumed << " (of " << mOut.dataSize()
-                        << "), read consumed: " << bwr.read_consumed << endl;
+        std::ostringstream logStream;
+        logStream << "Our err: " << (void*)(intptr_t)err
+                  << ", write consumed: " << bwr.write_consumed << " (of " << mOut.dataSize()
+                  << "), read consumed: " << bwr.read_consumed << "\n";
+        std::string message = logStream.str();
+        ALOGI("%s", message.c_str());
     }
 
     if (err >= NO_ERROR) {
@@ -1096,14 +1110,15 @@
             mIn.setDataPosition(0);
         }
         IF_LOG_COMMANDS() {
-            TextOutput::Bundle _b(alog);
-            alog << "Remaining data size: " << mOut.dataSize() << endl;
-            alog << "Received commands from driver: " << indent;
+            std::ostringstream logStream;
+            logStream << "Remaining data size: " << mOut.dataSize() << "\n";
+            logStream << "Received commands from driver: ";
             const void* cmds = mIn.data();
             const void* end = mIn.data() + mIn.dataSize();
-            alog << HexDump(cmds, mIn.dataSize()) << endl;
-            while (cmds < end) cmds = printReturnCommand(alog, cmds);
-            alog << dedent;
+            logStream << "\t" << HexDump(cmds, mIn.dataSize()) << "\n";
+            while (cmds < end) cmds = printReturnCommand(logStream, cmds);
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
         return NO_ERROR;
     }
@@ -1285,15 +1300,15 @@
             Parcel reply;
             status_t error;
             IF_LOG_TRANSACTIONS() {
-                TextOutput::Bundle _b(alog);
-                alog << "BR_TRANSACTION thr " << (void*)pthread_self()
-                    << " / obj " << tr.target.ptr << " / code "
-                    << TypeCode(tr.code) << ": " << indent << buffer
-                    << dedent << endl
-                    << "Data addr = "
-                    << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
-                    << ", offsets addr="
-                    << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
+                std::ostringstream logStream;
+                logStream << "BR_TRANSACTION thr " << (void*)pthread_self() << " / obj "
+                          << tr.target.ptr << " / code " << TypeCode(tr.code) << ": \t" << buffer
+                          << "\n"
+                          << "Data addr = " << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
+                          << ", offsets addr="
+                          << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << "\n";
+                std::string message = logStream.str();
+                ALOGI("%s", message.c_str());
             }
             if (tr.target.ptr) {
                 // We only have a weak reference on the target object, so we must first try to
@@ -1318,25 +1333,32 @@
                 LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                 if (error < NO_ERROR) reply.setError(error);
 
+                // b/238777741: clear buffer before we send the reply.
+                // Otherwise, there is a race where the client may
+                // receive the reply and send another transaction
+                // here and the space used by this transaction won't
+                // be freed for the client.
+                buffer.setDataSize(0);
+
                 constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;
                 sendReply(reply, (tr.flags & kForwardReplyFlags));
             } else {
                 if (error != OK) {
-                    alog << "oneway function results for code " << tr.code
-                         << " on binder at "
-                         << reinterpret_cast<void*>(tr.target.ptr)
-                         << " will be dropped but finished with status "
-                         << statusToString(error);
+                    std::ostringstream logStream;
+                    logStream << "oneway function results for code " << tr.code << " on binder at "
+                              << reinterpret_cast<void*>(tr.target.ptr)
+                              << " will be dropped but finished with status "
+                              << statusToString(error);
 
                     // ideally we could log this even when error == OK, but it
                     // causes too much logspam because some manually-written
                     // interfaces have clients that call methods which always
                     // write results, sometimes as oneway methods.
                     if (reply.dataSize() != 0) {
-                         alog << " and reply parcel size " << reply.dataSize();
+                        logStream << " and reply parcel size " << reply.dataSize();
                     }
-
-                    alog << endl;
+                    std::string message = logStream.str();
+                    ALOGI("%s", message.c_str());
                 }
                 LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
             }
@@ -1351,9 +1373,11 @@
             mPropagateWorkSource = origPropagateWorkSet;
 
             IF_LOG_TRANSACTIONS() {
-                TextOutput::Bundle _b(alog);
-                alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
-                    << tr.target.ptr << ": " << indent << reply << dedent << endl;
+                std::ostringstream logStream;
+                logStream << "BC_REPLY thr " << (void*)pthread_self() << " / obj " << tr.target.ptr
+                          << ": \t" << reply << "\n";
+                std::string message = logStream.str();
+                ALOGI("%s", message.c_str());
             }
 
         }
@@ -1474,7 +1498,10 @@
                                 const binder_size_t* /*objects*/, size_t /*objectsSize*/) {
     //ALOGI("Freeing parcel %p", &parcel);
     IF_LOG_COMMANDS() {
-        alog << "Writing BC_FREE_BUFFER for " << data << endl;
+        std::ostringstream logStream;
+        logStream << "Writing BC_FREE_BUFFER for " << data << "\n";
+        std::string message = logStream.str();
+        ALOGI("%s", message.c_str());
     }
     ALOG_ASSERT(data != NULL, "Called with NULL data");
     IPCThreadState* state = self();
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index c0a8d74..05db774 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "ServiceManager"
+#define LOG_TAG "ServiceManagerCppClient"
 
 #include <binder/IServiceManager.h>
 
@@ -380,6 +380,13 @@
     if (Status status = realGetService(name, &out); !status.isOk()) {
         ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(),
               status.toString8().c_str());
+        if (0 == ProcessState::self()->getThreadPoolMaxTotalThreadCount()) {
+            ALOGW("Got service, but may be racey because we could not wait efficiently for it. "
+                  "Threadpool has 0 guaranteed threads. "
+                  "Is the threadpool configured properly? "
+                  "See ProcessState::startThreadPool and "
+                  "ProcessState::setThreadPoolMaxThreadCount.");
+        }
         return nullptr;
     }
     if (out != nullptr) return out;
@@ -410,7 +417,9 @@
             if (waiter->mBinder != nullptr) return waiter->mBinder;
         }
 
-        ALOGW("Waited one second for %s (is service started? are binder threads started and available?)", name.c_str());
+        ALOGW("Waited one second for %s (is service started? Number of threads started in the "
+              "threadpool: %zu. Are binder threads started and available?)",
+              name.c_str(), ProcessState::self()->getThreadPoolMaxTotalThreadCount());
 
         // Handle race condition for lazy services. Here is what can happen:
         // - the service dies (not processed by init yet).
diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp
index 6eb7272..77e401f 100644
--- a/libs/binder/OS.cpp
+++ b/libs/binder/OS.cpp
@@ -17,6 +17,8 @@
 #include "OS.h"
 
 #include <android-base/file.h>
+#include <binder/RpcTransportRaw.h>
+#include <log/log.h>
 #include <string.h>
 
 using android::base::ErrnoError;
@@ -24,6 +26,9 @@
 
 namespace android {
 
+// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
+constexpr size_t kMaxFdsPerMsg = 253;
+
 Result<void> setNonBlocking(android::base::borrowed_fd fd) {
     int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
     if (flags == -1) {
@@ -48,4 +53,113 @@
     return OK;
 }
 
+status_t dupFileDescriptor(int oldFd, int* newFd) {
+    int ret = fcntl(oldFd, F_DUPFD_CLOEXEC, 0);
+    if (ret < 0) {
+        return -errno;
+    }
+
+    *newFd = ret;
+    return OK;
+}
+
+std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() {
+    return RpcTransportCtxFactoryRaw::make();
+}
+
+int sendMessageOnSocket(
+        const RpcTransportFd& socket, iovec* iovs, int niovs,
+        const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+    if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
+        if (ancillaryFds->size() > kMaxFdsPerMsg) {
+            errno = EINVAL;
+            return -1;
+        }
+
+        // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
+        // use memcpy.
+        int fds[kMaxFdsPerMsg];
+        for (size_t i = 0; i < ancillaryFds->size(); i++) {
+            fds[i] = std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i));
+        }
+        const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
+
+        alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
+
+        msghdr msg{
+                .msg_iov = iovs,
+                .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+                .msg_control = msgControlBuf,
+                .msg_controllen = sizeof(msgControlBuf),
+        };
+
+        cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
+        memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
+
+        msg.msg_controllen = CMSG_SPACE(fdsByteSize);
+        return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
+    }
+
+    msghdr msg{
+            .msg_iov = iovs,
+            // posix uses int, glibc uses size_t.  niovs is a
+            // non-negative int and can be cast to either.
+            .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+    };
+    return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+}
+
+int receiveMessageFromSocket(
+        const RpcTransportFd& socket, iovec* iovs, int niovs,
+        std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+    if (ancillaryFds != nullptr) {
+        int fdBuffer[kMaxFdsPerMsg];
+        alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
+
+        msghdr msg{
+                .msg_iov = iovs,
+                .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+                .msg_control = msgControlBuf,
+                .msg_controllen = sizeof(msgControlBuf),
+        };
+        ssize_t processSize = TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+        if (processSize < 0) {
+            return -1;
+        }
+
+        for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+                // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
+                // application devs to memcpy the data to ensure memory alignment.
+                size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
+                LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // validity check
+                memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
+                size_t fdCount = dataLen / sizeof(int);
+                ancillaryFds->reserve(ancillaryFds->size() + fdCount);
+                for (size_t i = 0; i < fdCount; i++) {
+                    ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i]));
+                }
+                break;
+            }
+        }
+
+        if (msg.msg_flags & MSG_CTRUNC) {
+            errno = EPIPE;
+            return -1;
+        }
+        return processSize;
+    }
+    msghdr msg{
+            .msg_iov = iovs,
+            // posix uses int, glibc uses size_t.  niovs is a
+            // non-negative int and can be cast to either.
+            .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+    };
+
+    return TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+}
+
 } // namespace android
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index e802e9c..0d38968 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -20,6 +20,7 @@
 
 #include <android-base/result.h>
 #include <android-base/unique_fd.h>
+#include <binder/RpcTransport.h>
 #include <utils/Errors.h>
 
 namespace android {
@@ -28,4 +29,16 @@
 
 status_t getRandomBytes(uint8_t* data, size_t size);
 
+status_t dupFileDescriptor(int oldFd, int* newFd);
+
+std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory();
+
+int sendMessageOnSocket(
+        const RpcTransportFd& socket, iovec* iovs, int niovs,
+        const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
+
+int receiveMessageFromSocket(
+        const RpcTransportFd& socket, iovec* iovs, int niovs,
+        std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
+
 } // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 8b5d118..07d0a65 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -48,6 +48,7 @@
 #include <utils/String8.h>
 #include <utils/misc.h>
 
+#include "OS.h"
 #include "RpcState.h"
 #include "Static.h"
 #include "Utils.h"
@@ -1438,7 +1439,8 @@
             case RpcSession::FileDescriptorTransportMode::NONE: {
                 return FDS_NOT_ALLOWED;
             }
-            case RpcSession::FileDescriptorTransportMode::UNIX: {
+            case RpcSession::FileDescriptorTransportMode::UNIX:
+            case RpcSession::FileDescriptorTransportMode::TRUSTY: {
                 if (rpcFields->mFds == nullptr) {
                     rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
                 }
@@ -1477,9 +1479,9 @@
 
 status_t Parcel::writeDupFileDescriptor(int fd)
 {
-    int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
-    if (dupFd < 0) {
-        return -errno;
+    int dupFd;
+    if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) {
+        return err;
     }
     status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/);
     if (err != OK) {
@@ -1496,9 +1498,9 @@
 
 status_t Parcel::writeDupParcelFileDescriptor(int fd)
 {
-    int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
-    if (dupFd < 0) {
-        return -errno;
+    int dupFd;
+    if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) {
+        return err;
     }
     status_t err = writeParcelFileDescriptor(dupFd, true /*takeOwnership*/);
     if (err != OK) {
@@ -2295,7 +2297,12 @@
         return BAD_TYPE;
     }
 
-    val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0));
+    int dupFd;
+    if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) {
+        return BAD_VALUE;
+    }
+
+    val->reset(dupFd);
 
     if (val->get() < 0) {
         return BAD_VALUE;
@@ -2312,7 +2319,12 @@
         return BAD_TYPE;
     }
 
-    val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0));
+    int dupFd;
+    if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) {
+        return BAD_VALUE;
+    }
+
+    val->reset(dupFd);
 
     if (val->get() < 0) {
         return BAD_VALUE;
@@ -2628,8 +2640,7 @@
     return OK;
 }
 
-void Parcel::print(TextOutput& to, uint32_t /*flags*/) const
-{
+void Parcel::print(std::ostream& to, uint32_t /*flags*/) const {
     to << "Parcel(";
 
     if (errorCheck() != NO_ERROR) {
@@ -2637,7 +2648,7 @@
         to << "Error: " << (void*)(intptr_t)err << " \"" << strerror(-err) << "\"";
     } else if (dataSize() > 0) {
         const uint8_t* DATA = data();
-        to << indent << HexDump(DATA, dataSize()) << dedent;
+        to << "\t" << HexDump(DATA, dataSize());
 #ifdef BINDER_WITH_KERNEL_IPC
         if (const auto* kernelFields = maybeKernelFields()) {
             const binder_size_t* OBJS = kernelFields->mObjects;
@@ -2645,8 +2656,7 @@
             for (size_t i = 0; i < N; i++) {
                 const flat_binder_object* flat =
                         reinterpret_cast<const flat_binder_object*>(DATA + OBJS[i]);
-                to << endl
-                   << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
+                to << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
                    << TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder;
             }
         }
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 49be4dd..83d0de7 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -37,6 +37,7 @@
 #include "OS.h"
 #include "RpcSocketAddress.h"
 #include "RpcState.h"
+#include "RpcTransportUtils.h"
 #include "RpcWireFormat.h"
 #include "Utils.h"
 
@@ -55,12 +56,16 @@
 sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
     // Default is without TLS.
     if (rpcTransportCtxFactory == nullptr)
-        rpcTransportCtxFactory = RpcTransportCtxFactoryRaw::make();
+        rpcTransportCtxFactory = makeDefaultRpcTransportCtxFactory();
     auto ctx = rpcTransportCtxFactory->newServerCtx();
     if (ctx == nullptr) return nullptr;
     return sp<RpcServer>::make(std::move(ctx));
 }
 
+status_t RpcServer::setupUnixDomainSocketBootstrapServer(unique_fd bootstrapFd) {
+    return setupExternalServer(std::move(bootstrapFd), &RpcServer::recvmsgSocketConnection);
+}
+
 status_t RpcServer::setupUnixDomainServer(const char* path) {
     return setupSocketServer(UnixSocketAddress(path));
 }
@@ -86,7 +91,7 @@
         LOG_ALWAYS_FATAL_IF(socketAddress.addr()->sa_family != AF_INET, "expecting inet");
         sockaddr_in addr{};
         socklen_t len = sizeof(addr);
-        if (0 != getsockname(mServer.get(), reinterpret_cast<sockaddr*>(&addr), &len)) {
+        if (0 != getsockname(mServer.fd.get(), reinterpret_cast<sockaddr*>(&addr), &len)) {
             int savedErrno = errno;
             ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(),
                   strerror(savedErrno));
@@ -177,11 +182,50 @@
     rpcJoinIfSingleThreaded(*mJoinThread);
 }
 
+status_t RpcServer::acceptSocketConnection(const RpcServer& server, RpcTransportFd* out) {
+    RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY(
+            accept4(server.mServer.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK))));
+    if (clientSocket.fd < 0) {
+        int savedErrno = errno;
+        ALOGE("Could not accept4 socket: %s", strerror(savedErrno));
+        return -savedErrno;
+    }
+
+    *out = std::move(clientSocket);
+    return OK;
+}
+
+status_t RpcServer::recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out) {
+    int zero = 0;
+    iovec iov{&zero, sizeof(zero)};
+    std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+
+    if (receiveMessageFromSocket(server.mServer, &iov, 1, &fds) < 0) {
+        int savedErrno = errno;
+        ALOGE("Failed recvmsg: %s", strerror(savedErrno));
+        return -savedErrno;
+    }
+    if (fds.size() != 1) {
+        ALOGE("Expected exactly one fd from recvmsg, got %zu", fds.size());
+        return -EINVAL;
+    }
+
+    unique_fd fd(std::move(std::get<unique_fd>(fds.back())));
+    if (auto res = setNonBlocking(fd); !res.ok()) {
+        ALOGE("Failed setNonBlocking: %s", res.error().message().c_str());
+        return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
+    }
+
+    *out = RpcTransportFd(std::move(fd));
+    return OK;
+}
+
 void RpcServer::join() {
 
     {
         RpcMutexLockGuard _l(mLock);
-        LOG_ALWAYS_FATAL_IF(!mServer.ok(), "RpcServer must be setup to join.");
+        LOG_ALWAYS_FATAL_IF(!mServer.fd.ok(), "RpcServer must be setup to join.");
+        LOG_ALWAYS_FATAL_IF(mAcceptFn == nullptr, "RpcServer must have an accept() function");
         LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
         mJoinThreadRunning = true;
         mShutdownTrigger = FdTrigger::make();
@@ -192,26 +236,25 @@
     while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) {
         std::array<uint8_t, kRpcAddressSize> addr;
         static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small");
-
         socklen_t addrLen = addr.size();
-        unique_fd clientFd(
-                TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(addr.data()),
-                                           &addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK)));
 
-        LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(sockaddr_storage)),
-                            "Truncated address");
-
-        if (clientFd < 0) {
-            ALOGE("Could not accept4 socket: %s", strerror(errno));
+        RpcTransportFd clientSocket;
+        if (mAcceptFn(*this, &clientSocket) != OK) {
             continue;
         }
-        LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+        if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
+                        &addrLen)) {
+            ALOGE("Could not getpeername socket: %s", strerror(errno));
+            continue;
+        }
+
+        LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
 
         {
             RpcMutexLockGuard _l(mLock);
             RpcMaybeThread thread =
                     RpcMaybeThread(&RpcServer::establishConnection,
-                                   sp<RpcServer>::fromExisting(this), std::move(clientFd), addr,
+                                   sp<RpcServer>::fromExisting(this), std::move(clientSocket), addr,
                                    addrLen, RpcSession::join);
 
             auto& threadRef = mConnectingThreads[thread.get_id()];
@@ -296,7 +339,7 @@
 }
 
 void RpcServer::establishConnection(
-        sp<RpcServer>&& server, base::unique_fd clientFd, std::array<uint8_t, kRpcAddressSize> addr,
+        sp<RpcServer>&& server, RpcTransportFd clientFd, std::array<uint8_t, kRpcAddressSize> addr,
         size_t addrLen,
         std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn) {
     // mShutdownTrigger can only be cleared once connection threads have joined.
@@ -306,7 +349,7 @@
 
     status_t status = OK;
 
-    int clientFdForLog = clientFd.get();
+    int clientFdForLog = clientFd.fd.get();
     auto client = server->mCtx->newTransport(std::move(clientFd), server->mShutdownTrigger.get());
     if (client == nullptr) {
         ALOGE("Dropping accept4()-ed socket because sslAccept fails");
@@ -488,15 +531,15 @@
     LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
     LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
 
-    unique_fd serverFd(TEMP_FAILURE_RETRY(
-            socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
-    if (serverFd == -1) {
+    RpcTransportFd transportFd(unique_fd(TEMP_FAILURE_RETRY(
+            socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))));
+    if (!transportFd.fd.ok()) {
         int savedErrno = errno;
         ALOGE("Could not create socket: %s", strerror(savedErrno));
         return -savedErrno;
     }
 
-    if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
+    if (0 != TEMP_FAILURE_RETRY(bind(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
         int savedErrno = errno;
         ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
         return -savedErrno;
@@ -506,7 +549,7 @@
     // the backlog is increased to a large number.
     // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced
     //  to 1.
-    if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 50 /*backlog*/))) {
+    if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) {
         int savedErrno = errno;
         ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
         return -savedErrno;
@@ -514,7 +557,7 @@
 
     LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
 
-    if (status_t status = setupExternalServer(std::move(serverFd)); status != OK) {
+    if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) {
         ALOGE("Another thread has set up server while calling setupSocketServer. Race?");
         return status;
     }
@@ -542,22 +585,39 @@
 
 bool RpcServer::hasServer() {
     RpcMutexLockGuard _l(mLock);
-    return mServer.ok();
+    return mServer.fd.ok();
 }
 
 unique_fd RpcServer::releaseServer() {
     RpcMutexLockGuard _l(mLock);
-    return std::move(mServer);
+    return std::move(mServer.fd);
 }
 
-status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
+status_t RpcServer::setupExternalServer(
+        base::unique_fd serverFd,
+        std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn) {
     RpcMutexLockGuard _l(mLock);
-    if (mServer.ok()) {
+    if (mServer.fd.ok()) {
         ALOGE("Each RpcServer can only have one server.");
         return INVALID_OPERATION;
     }
     mServer = std::move(serverFd);
+    mAcceptFn = std::move(acceptFn);
     return OK;
 }
 
+status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
+    return setupExternalServer(std::move(serverFd), &RpcServer::acceptSocketConnection);
+}
+
+bool RpcServer::hasActiveRequests() {
+    RpcMutexLockGuard _l(mLock);
+    for (const auto& [_, session] : mSessions) {
+        if (session->hasActiveRequests()) {
+            return true;
+        }
+    }
+    return !mServer.isInPollingState();
+}
+
 } // namespace android
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index d347262..7d6bcfc 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -41,6 +41,7 @@
 #include "OS.h"
 #include "RpcSocketAddress.h"
 #include "RpcState.h"
+#include "RpcTransportUtils.h"
 #include "RpcWireFormat.h"
 #include "Utils.h"
 
@@ -68,7 +69,7 @@
 
 sp<RpcSession> RpcSession::make() {
     // Default is without TLS.
-    return make(RpcTransportCtxFactoryRaw::make());
+    return make(makeDefaultRpcTransportCtxFactory());
 }
 
 sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
@@ -147,6 +148,34 @@
     return setupSocketClient(UnixSocketAddress(path));
 }
 
+status_t RpcSession::setupUnixDomainSocketBootstrapClient(unique_fd bootstrapFd) {
+    mBootstrapTransport =
+            mCtx->newTransport(RpcTransportFd(std::move(bootstrapFd)), mShutdownTrigger.get());
+    return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) {
+        int socks[2];
+        if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, socks) < 0) {
+            int savedErrno = errno;
+            ALOGE("Failed socketpair: %s", strerror(savedErrno));
+            return -savedErrno;
+        }
+        unique_fd clientFd(socks[0]), serverFd(socks[1]);
+
+        int zero = 0;
+        iovec iov{&zero, sizeof(zero)};
+        std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+        fds.push_back(std::move(serverFd));
+
+        status_t status = mBootstrapTransport->interruptableWriteFully(mShutdownTrigger.get(), &iov,
+                                                                       1, std::nullopt, &fds);
+        if (status != OK) {
+            ALOGE("Failed to send fd over bootstrap transport: %s", strerror(-status));
+            return status;
+        }
+
+        return initAndAddConnection(RpcTransportFd(std::move(clientFd)), sessionId, incoming);
+    });
+}
+
 status_t RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
     return setupSocketClient(VsockSocketAddress(cid, port));
 }
@@ -162,7 +191,8 @@
     return NAME_NOT_FOUND;
 }
 
-status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
+status_t RpcSession::setupPreconnectedClient(base::unique_fd fd,
+                                             std::function<unique_fd()>&& request) {
     return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
         if (!fd.ok()) {
             fd = request();
@@ -172,7 +202,9 @@
             ALOGE("setupPreconnectedClient: %s", res.error().message().c_str());
             return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
         }
-        status_t status = initAndAddConnection(std::move(fd), sessionId, incoming);
+
+        RpcTransportFd transportFd(std::move(fd));
+        status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming);
         fd = unique_fd(); // Explicitly reset after move to avoid analyzer warning.
         return status;
     });
@@ -190,7 +222,8 @@
         return -savedErrno;
     }
 
-    auto server = mCtx->newTransport(std::move(serverFd), mShutdownTrigger.get());
+    RpcTransportFd transportFd(std::move(serverFd));
+    auto server = mCtx->newTransport(std::move(transportFd), mShutdownTrigger.get());
     if (server == nullptr) {
         ALOGE("Unable to set up RpcTransport");
         return UNKNOWN_ERROR;
@@ -291,16 +324,18 @@
 }
 
 void RpcSession::WaitForShutdownListener::onSessionIncomingThreadEnded() {
+    mShutdownCount += 1;
     mCv.notify_all();
 }
 
 void RpcSession::WaitForShutdownListener::waitForShutdown(RpcMutexUniqueLock& lock,
                                                           const sp<RpcSession>& session) {
-    while (session->mConnections.mIncoming.size() > 0) {
+    while (mShutdownCount < session->mConnections.mMaxIncoming) {
         if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) {
             ALOGE("Waiting for RpcSession to shut down (1s w/o progress): %zu incoming connections "
-                  "still.",
-                  session->mConnections.mIncoming.size());
+                  "still %zu/%zu fully shutdown.",
+                  session->mConnections.mIncoming.size(), mShutdownCount.load(),
+                  session->mConnections.mMaxIncoming);
         }
     }
 }
@@ -484,6 +519,9 @@
         mProtocolVersion = oldProtocolVersion;
 
         mConnections = {};
+
+        // clear mStartedSetup so that we can reuse this RpcSession
+        mStartedSetup = false;
     });
 
     if (status_t status = connectAndInit({}, false /*incoming*/); status != OK) return status;
@@ -569,12 +607,14 @@
             return -savedErrno;
         }
 
-        if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+        RpcTransportFd transportFd(std::move(serverFd));
+
+        if (0 != TEMP_FAILURE_RETRY(connect(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
             int connErrno = errno;
             if (connErrno == EAGAIN || connErrno == EINPROGRESS) {
                 // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or
                 // EINPROGRESS (for others). Call poll() and getsockopt() to get the error.
-                status_t pollStatus = mShutdownTrigger->triggerablePoll(serverFd, POLLOUT);
+                status_t pollStatus = mShutdownTrigger->triggerablePoll(transportFd, POLLOUT);
                 if (pollStatus != OK) {
                     ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s",
                           statusToString(pollStatus).c_str());
@@ -582,8 +622,8 @@
                 }
                 // Set connErrno to the errno that connect() would have set if the fd were blocking.
                 socklen_t connErrnoLen = sizeof(connErrno);
-                int ret =
-                        getsockopt(serverFd.get(), SOL_SOCKET, SO_ERROR, &connErrno, &connErrnoLen);
+                int ret = getsockopt(transportFd.fd.get(), SOL_SOCKET, SO_ERROR, &connErrno,
+                                     &connErrnoLen);
                 if (ret == -1) {
                     int savedErrno = errno;
                     ALOGE("Could not getsockopt() after connect() on non-blocking socket: %s. "
@@ -605,16 +645,17 @@
                 return -connErrno;
             }
         }
-        LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+        LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(),
+                       transportFd.fd.get());
 
-        return initAndAddConnection(std::move(serverFd), sessionId, incoming);
+        return initAndAddConnection(std::move(transportFd), sessionId, incoming);
     }
 
     ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
     return UNKNOWN_ERROR;
 }
 
-status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_t>& sessionId,
+status_t RpcSession::initAndAddConnection(RpcTransportFd fd, const std::vector<uint8_t>& sessionId,
                                           bool incoming) {
     LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr);
     auto server = mCtx->newTransport(std::move(fd), mShutdownTrigger.get());
@@ -942,4 +983,24 @@
     }
 }
 
+bool RpcSession::hasActiveConnection(const std::vector<sp<RpcConnection>>& connections) {
+    for (const auto& connection : connections) {
+        if (connection->exclusiveTid != std::nullopt && !connection->rpcTransport->isWaiting()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool RpcSession::hasActiveRequests() {
+    RpcMutexUniqueLock _l(mMutex);
+    if (hasActiveConnection(mConnections.mIncoming)) {
+        return true;
+    }
+    if (hasActiveConnection(mConnections.mOutgoing)) {
+        return true;
+    }
+    return mConnections.mWaitingThreads != 0;
+}
+
 } // namespace android
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index c0e36c4..b27f102 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -41,7 +41,7 @@
 #if RPC_FLAKE_PRONE
 void rpcMaybeWaitToFlake() {
     [[clang::no_destroy]] static std::random_device r;
-    [[clang::no_destroy]] static std::mutex m;
+    [[clang::no_destroy]] static RpcMutex m;
     unsigned num;
     {
         RpcMutexLockGuard lock(m);
@@ -56,6 +56,7 @@
         case RpcSession::FileDescriptorTransportMode::NONE:
             return false;
         case RpcSession::FileDescriptorTransportMode::UNIX:
+        case RpcSession::FileDescriptorTransportMode::TRUSTY:
             return true;
     }
 }
@@ -886,6 +887,7 @@
                 it->second.asyncTodo.push(BinderNode::AsyncTodo{
                         .ref = target,
                         .data = std::move(transactionData),
+                        .ancillaryFds = std::move(ancillaryFds),
                         .asyncNumber = transaction->asyncNumber,
                 });
 
@@ -1046,6 +1048,7 @@
 
                 // reset up arguments
                 transactionData = std::move(todo.data);
+                ancillaryFds = std::move(todo.ancillaryFds);
                 LOG_ALWAYS_FATAL_IF(target != todo.ref,
                                     "async list should be associated with a binder");
 
@@ -1205,6 +1208,20 @@
                                              rpcFields->mFds->size(), kMaxFdsPerMsg);
                     return BAD_VALUE;
                 }
+                break;
+            }
+            case RpcSession::FileDescriptorTransportMode::TRUSTY: {
+                // Keep this in sync with trusty_ipc.h!!!
+                // We could import that file here on Trusty, but it's not
+                // available on Android
+                constexpr size_t kMaxFdsPerMsg = 8;
+                if (rpcFields->mFds->size() > kMaxFdsPerMsg) {
+                    *errorMsg = StringPrintf("Too many file descriptors in Parcel for Trusty "
+                                             "IPC connection: %zu (max is %zu)",
+                                             rpcFields->mFds->size(), kMaxFdsPerMsg);
+                    return BAD_VALUE;
+                }
+                break;
             }
         }
     }
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 7aab5ee..ac86585 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -250,6 +250,7 @@
         struct AsyncTodo {
             sp<IBinder> ref;
             CommandData data;
+            std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
             uint64_t asyncNumber = 0;
 
             bool operator<(const AsyncTodo& o) const {
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 51326f6..1912d14 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -23,6 +23,7 @@
 #include <binder/RpcTransportRaw.h>
 
 #include "FdTrigger.h"
+#include "OS.h"
 #include "RpcState.h"
 #include "RpcTransportUtils.h"
 
@@ -30,17 +31,14 @@
 
 namespace {
 
-// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
-constexpr size_t kMaxFdsPerMsg = 253;
-
 // RpcTransport with TLS disabled.
 class RpcTransportRaw : public RpcTransport {
 public:
-    explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
+    explicit RpcTransportRaw(android::RpcTransportFd socket) : mSocket(std::move(socket)) {}
     status_t pollRead(void) override {
         uint8_t buf;
         ssize_t ret = TEMP_FAILURE_RETRY(
-                ::recv(mSocket.get(), &buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT));
+                ::recv(mSocket.fd.get(), &buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT));
         if (ret < 0) {
             int savedErrno = errno;
             if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) {
@@ -63,60 +61,12 @@
             override {
         bool sentFds = false;
         auto send = [&](iovec* iovs, int niovs) -> ssize_t {
-            if (ancillaryFds != nullptr && !ancillaryFds->empty() && !sentFds) {
-                if (ancillaryFds->size() > kMaxFdsPerMsg) {
-                    // This shouldn't happen because we check the FD count in RpcState.
-                    ALOGE("Saw too many file descriptors in RpcTransportCtxRaw: %zu (max is %zu). "
-                          "Aborting session.",
-                          ancillaryFds->size(), kMaxFdsPerMsg);
-                    errno = EINVAL;
-                    return -1;
-                }
-
-                // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
-                // use memcpy.
-                int fds[kMaxFdsPerMsg];
-                for (size_t i = 0; i < ancillaryFds->size(); i++) {
-                    fds[i] = std::visit([](const auto& fd) { return fd.get(); },
-                                        ancillaryFds->at(i));
-                }
-                const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
-
-                alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
-
-                msghdr msg{
-                        .msg_iov = iovs,
-                        .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
-                        .msg_control = msgControlBuf,
-                        .msg_controllen = sizeof(msgControlBuf),
-                };
-
-                cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
-                cmsg->cmsg_level = SOL_SOCKET;
-                cmsg->cmsg_type = SCM_RIGHTS;
-                cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
-                memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
-
-                msg.msg_controllen = CMSG_SPACE(fdsByteSize);
-
-                ssize_t processedSize = TEMP_FAILURE_RETRY(
-                        sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
-                if (processedSize > 0) {
-                    sentFds = true;
-                }
-                return processedSize;
-            }
-
-            msghdr msg{
-                    .msg_iov = iovs,
-                    // posix uses int, glibc uses size_t.  niovs is a
-                    // non-negative int and can be cast to either.
-                    .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
-            };
-            return TEMP_FAILURE_RETRY(sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
+            int ret = sendMessageOnSocket(mSocket, iovs, niovs, sentFds ? nullptr : ancillaryFds);
+            sentFds |= ret > 0;
+            return ret;
         };
-        return interruptableReadOrWrite(mSocket.get(), fdTrigger, iovs, niovs, send, "sendmsg",
-                                        POLLOUT, altPoll);
+        return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT,
+                                        altPoll);
     }
 
     status_t interruptableReadFully(
@@ -124,68 +74,23 @@
             const std::optional<android::base::function_ref<status_t()>>& altPoll,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
         auto recv = [&](iovec* iovs, int niovs) -> ssize_t {
-            if (ancillaryFds != nullptr) {
-                int fdBuffer[kMaxFdsPerMsg];
-                alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
-
-                msghdr msg{
-                        .msg_iov = iovs,
-                        .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
-                        .msg_control = msgControlBuf,
-                        .msg_controllen = sizeof(msgControlBuf),
-                };
-                ssize_t processSize =
-                        TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
-                if (processSize < 0) {
-                    return -1;
-                }
-
-                for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;
-                     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-                    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
-                        // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
-                        // application devs to memcpy the data to ensure memory alignment.
-                        size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
-                        LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // sanity check
-                        memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
-                        size_t fdCount = dataLen / sizeof(int);
-                        ancillaryFds->reserve(ancillaryFds->size() + fdCount);
-                        for (size_t i = 0; i < fdCount; i++) {
-                            ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i]));
-                        }
-                        break;
-                    }
-                }
-
-                if (msg.msg_flags & MSG_CTRUNC) {
-                    ALOGE("msg was truncated. Aborting session.");
-                    errno = EPIPE;
-                    return -1;
-                }
-
-                return processSize;
-            }
-            msghdr msg{
-                    .msg_iov = iovs,
-                    // posix uses int, glibc uses size_t.  niovs is a
-                    // non-negative int and can be cast to either.
-                    .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
-            };
-            return TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL));
+            return receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds);
         };
-        return interruptableReadOrWrite(mSocket.get(), fdTrigger, iovs, niovs, recv, "recvmsg",
-                                        POLLIN, altPoll);
+        return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN,
+                                        altPoll);
     }
 
+    virtual bool isWaiting() { return mSocket.isInPollingState(); }
+
 private:
-    base::unique_fd mSocket;
+    android::RpcTransportFd mSocket;
 };
 
 // RpcTransportCtx with TLS disabled.
 class RpcTransportCtxRaw : public RpcTransportCtx {
 public:
-    std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, FdTrigger*) const {
-        return std::make_unique<RpcTransportRaw>(std::move(fd));
+    std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket, FdTrigger*) const {
+        return std::make_unique<RpcTransportRaw>(std::move(socket));
     }
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
new file mode 100644
index 0000000..453279c
--- /dev/null
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcTransportTipcAndroid"
+
+#include <binder/RpcSession.h>
+#include <binder/RpcTransportTipcAndroid.h>
+#include <log/log.h>
+#include <poll.h>
+#include <trusty/tipc.h>
+
+#include "FdTrigger.h"
+#include "RpcState.h"
+#include "RpcTransportUtils.h"
+
+using android::base::Error;
+using android::base::Result;
+
+namespace android {
+
+namespace {
+
+// RpcTransport for writing Trusty IPC clients in Android.
+class RpcTransportTipcAndroid : public RpcTransport {
+public:
+    explicit RpcTransportTipcAndroid(android::RpcTransportFd socket) : mSocket(std::move(socket)) {}
+
+    status_t pollRead() override {
+        if (mReadBufferPos < mReadBufferSize) {
+            // We have more data in the read buffer
+            return OK;
+        }
+
+        // Trusty IPC device is not a socket, so MSG_PEEK is not available
+        pollfd pfd{.fd = mSocket.fd.get(), .events = static_cast<int16_t>(POLLIN), .revents = 0};
+        ssize_t ret = TEMP_FAILURE_RETRY(::poll(&pfd, 1, 0));
+        if (ret < 0) {
+            int savedErrno = errno;
+            if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) {
+                return WOULD_BLOCK;
+            }
+
+            LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno));
+            return -savedErrno;
+        }
+
+        if (pfd.revents & POLLNVAL) {
+            return BAD_VALUE;
+        }
+        if (pfd.revents & POLLERR) {
+            return DEAD_OBJECT;
+        }
+        if (pfd.revents & POLLHUP) {
+            return DEAD_OBJECT;
+        }
+        if (pfd.revents & POLLIN) {
+            return OK;
+        }
+
+        return WOULD_BLOCK;
+    }
+
+    status_t interruptableWriteFully(
+            FdTrigger* fdTrigger, iovec* iovs, int niovs,
+            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
+            override {
+        auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
+            // TODO: send ancillaryFds. For now, we just abort if anyone tries
+            // to send any.
+            LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(),
+                                "File descriptors are not supported on Trusty yet");
+            return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0));
+        };
+        return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn, "tipc_send",
+                                        POLLOUT, altPoll);
+    }
+
+    status_t interruptableReadFully(
+            FdTrigger* fdTrigger, iovec* iovs, int niovs,
+            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/)
+            override {
+        auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
+            // Fill the read buffer at most once per readFn call, then try to
+            // return as much of it as possible. If the input iovecs are spread
+            // across multiple messages that require multiple fillReadBuffer
+            // calls, we expect the caller to advance the iovecs past the first
+            // read and call readFn as many times as needed to get all the data
+            status_t ret = fillReadBuffer();
+            if (ret != OK) {
+                // We need to emulate a Linux read call, which sets errno on
+                // error and returns -1
+                errno = -ret;
+                return -1;
+            }
+
+            ssize_t processSize = 0;
+            for (size_t i = 0; i < niovs && mReadBufferPos < mReadBufferSize; i++) {
+                auto& iov = iovs[i];
+                size_t numBytes = std::min(iov.iov_len, mReadBufferSize - mReadBufferPos);
+                memcpy(iov.iov_base, mReadBuffer.get() + mReadBufferPos, numBytes);
+                mReadBufferPos += numBytes;
+                processSize += numBytes;
+            }
+
+            return processSize;
+        };
+        return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, readFn, "read", POLLIN,
+                                        altPoll);
+    }
+
+    bool isWaiting() override { return mSocket.isInPollingState(); }
+
+private:
+    status_t fillReadBuffer() {
+        if (mReadBufferPos < mReadBufferSize) {
+            return OK;
+        }
+
+        if (!mReadBuffer) {
+            // Guarantee at least kDefaultBufferSize bytes
+            mReadBufferCapacity = std::max(mReadBufferCapacity, kDefaultBufferSize);
+            mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]);
+            if (!mReadBuffer) {
+                return NO_MEMORY;
+            }
+        }
+
+        // Reset the size and position in case we have to exit with an error.
+        // After we read a message into the buffer, we update the size
+        // with the actual value.
+        mReadBufferPos = 0;
+        mReadBufferSize = 0;
+
+        while (true) {
+            ssize_t processSize = TEMP_FAILURE_RETRY(
+                    read(mSocket.fd.get(), mReadBuffer.get(), mReadBufferCapacity));
+            if (processSize == 0) {
+                return DEAD_OBJECT;
+            } else if (processSize < 0) {
+                int savedErrno = errno;
+                if (savedErrno == EMSGSIZE) {
+                    // Buffer was too small, double it and retry
+                    if (__builtin_mul_overflow(mReadBufferCapacity, 2, &mReadBufferCapacity)) {
+                        return NO_MEMORY;
+                    }
+                    mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]);
+                    if (!mReadBuffer) {
+                        return NO_MEMORY;
+                    }
+                    continue;
+                } else {
+                    LOG_RPC_DETAIL("RpcTransport fillBuffer(): %s", strerror(savedErrno));
+                    return -savedErrno;
+                }
+            } else {
+                mReadBufferSize = static_cast<size_t>(processSize);
+                return OK;
+            }
+        }
+    }
+
+    RpcTransportFd mSocket;
+
+    // For now, we copy all the input data into a temporary buffer because
+    // we might get multiple interruptableReadFully calls per message, but
+    // the tipc device only allows one read call. We read every message into
+    // this temporary buffer, then return pieces of it from our method.
+    //
+    // The special transaction GET_MAX_THREADS takes 40 bytes, so the default
+    // size should start pretty high.
+    static constexpr size_t kDefaultBufferSize = 64;
+    std::unique_ptr<uint8_t[]> mReadBuffer;
+    size_t mReadBufferPos = 0;
+    size_t mReadBufferSize = 0;
+    size_t mReadBufferCapacity = 0;
+};
+
+// RpcTransportCtx for Trusty.
+class RpcTransportCtxTipcAndroid : public RpcTransportCtx {
+public:
+    std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd fd,
+                                               FdTrigger*) const override {
+        return std::make_unique<RpcTransportTipcAndroid>(std::move(fd));
+    }
+    std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
+};
+
+} // namespace
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newServerCtx() const {
+    return std::make_unique<RpcTransportCtxTipcAndroid>();
+}
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newClientCtx() const {
+    return std::make_unique<RpcTransportCtxTipcAndroid>();
+}
+
+const char* RpcTransportCtxFactoryTipcAndroid::toCString() const {
+    return "trusty";
+}
+
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcAndroid::make() {
+    return std::unique_ptr<RpcTransportCtxFactoryTipcAndroid>(
+            new RpcTransportCtxFactoryTipcAndroid());
+}
+
+} // namespace android
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 09b5c17..3e98ecc 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -182,8 +182,8 @@
     // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
     // return error. Also return error if |fdTrigger| is triggered before or during poll().
     status_t pollForSslError(
-            android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger, const char* fnString,
-            int additionalEvent,
+            const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger,
+            const char* fnString, int additionalEvent,
             const std::optional<android::base::function_ref<status_t()>>& altPoll) {
         switch (sslError) {
             case SSL_ERROR_WANT_READ:
@@ -198,7 +198,7 @@
 private:
     bool mHandled = false;
 
-    status_t handlePoll(int event, android::base::borrowed_fd fd, FdTrigger* fdTrigger,
+    status_t handlePoll(int event, const android::RpcTransportFd& fd, FdTrigger* fdTrigger,
                         const char* fnString,
                         const std::optional<android::base::function_ref<status_t()>>& altPoll) {
         status_t ret;
@@ -277,7 +277,7 @@
 
 class RpcTransportTls : public RpcTransport {
 public:
-    RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
+    RpcTransportTls(RpcTransportFd socket, Ssl ssl)
           : mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
     status_t pollRead(void) override;
     status_t interruptableWriteFully(
@@ -290,8 +290,10 @@
             const std::optional<android::base::function_ref<status_t()>>& altPoll,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override;
 
+    bool isWaiting() { return mSocket.isInPollingState(); };
+
 private:
-    android::base::unique_fd mSocket;
+    android::RpcTransportFd mSocket;
     Ssl mSsl;
 };
 
@@ -350,7 +352,7 @@
             int sslError = mSsl.getError(writeSize);
             // TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be
             //   triggerablePoll()-ed. Then additionalEvent is no longer necessary.
-            status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+            status_t pollStatus = errorQueue.pollForSslError(mSocket, sslError, fdTrigger,
                                                              "SSL_write", POLLIN, altPoll);
             if (pollStatus != OK) return pollStatus;
             // Do not advance buffer. Try SSL_write() again.
@@ -398,7 +400,7 @@
                 return DEAD_OBJECT;
             }
             int sslError = mSsl.getError(readSize);
-            status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+            status_t pollStatus = errorQueue.pollForSslError(mSocket, sslError, fdTrigger,
                                                              "SSL_read", 0, altPoll);
             if (pollStatus != OK) return pollStatus;
             // Do not advance buffer. Try SSL_read() again.
@@ -409,8 +411,8 @@
 }
 
 // For |ssl|, set internal FD to |fd|, and do handshake. Handshake is triggerable by |fdTrigger|.
-bool setFdAndDoHandshake(Ssl* ssl, android::base::borrowed_fd fd, FdTrigger* fdTrigger) {
-    bssl::UniquePtr<BIO> bio = newSocketBio(fd);
+bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket, FdTrigger* fdTrigger) {
+    bssl::UniquePtr<BIO> bio = newSocketBio(socket.fd);
     TEST_AND_RETURN(false, bio != nullptr);
     auto [_, errorQueue] = ssl->call(SSL_set_bio, bio.get(), bio.get());
     (void)bio.release(); // SSL_set_bio takes ownership.
@@ -430,7 +432,7 @@
             return false;
         }
         int sslError = ssl->getError(ret);
-        status_t pollStatus = errorQueue.pollForSslError(fd, sslError, fdTrigger,
+        status_t pollStatus = errorQueue.pollForSslError(socket, sslError, fdTrigger,
                                                          "SSL_do_handshake", 0, std::nullopt);
         if (pollStatus != OK) return false;
     }
@@ -442,7 +444,7 @@
               typename = std::enable_if_t<std::is_base_of_v<RpcTransportCtxTls, Impl>>>
     static std::unique_ptr<RpcTransportCtxTls> create(
             std::shared_ptr<RpcCertificateVerifier> verifier, RpcAuth* auth);
-    std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd,
+    std::unique_ptr<RpcTransport> newTransport(RpcTransportFd fd,
                                                FdTrigger* fdTrigger) const override;
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override;
 
@@ -513,15 +515,15 @@
     return ret;
 }
 
-std::unique_ptr<RpcTransport> RpcTransportCtxTls::newTransport(android::base::unique_fd fd,
+std::unique_ptr<RpcTransport> RpcTransportCtxTls::newTransport(android::RpcTransportFd socket,
                                                                FdTrigger* fdTrigger) const {
     bssl::UniquePtr<SSL> ssl(SSL_new(mCtx.get()));
     TEST_AND_RETURN(nullptr, ssl != nullptr);
     Ssl wrapped(std::move(ssl));
 
     preHandshake(&wrapped);
-    TEST_AND_RETURN(nullptr, setFdAndDoHandshake(&wrapped, fd, fdTrigger));
-    return std::make_unique<RpcTransportTls>(std::move(fd), std::move(wrapped));
+    TEST_AND_RETURN(nullptr, setFdAndDoHandshake(&wrapped, socket, fdTrigger));
+    return std::make_unique<RpcTransportTls>(std::move(socket), std::move(wrapped));
 }
 
 class RpcTransportCtxTlsServer : public RpcTransportCtxTls {
diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h
index 00cb2af..32f0db8 100644
--- a/libs/binder/RpcTransportUtils.h
+++ b/libs/binder/RpcTransportUtils.h
@@ -25,8 +25,8 @@
 
 template <typename SendOrReceive>
 status_t interruptableReadOrWrite(
-        int socketFd, FdTrigger* fdTrigger, iovec* iovs, int niovs, SendOrReceive sendOrReceiveFun,
-        const char* funName, int16_t event,
+        const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs,
+        SendOrReceive sendOrReceiveFun, const char* funName, int16_t event,
         const std::optional<android::base::function_ref<status_t()>>& altPoll) {
     MAYBE_WAIT_IN_FLAKE_MODE;
 
@@ -99,7 +99,7 @@
                 return DEAD_OBJECT;
             }
         } else {
-            if (status_t status = fdTrigger->triggerablePoll(socketFd, event); status != OK)
+            if (status_t status = fdTrigger->triggerablePoll(socket, event); status != OK)
                 return status;
             if (!havePolled) havePolled = true;
         }
diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp
new file mode 100644
index 0000000..3b53b05
--- /dev/null
+++ b/libs/binder/RpcTrusty.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcTrusty"
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcTransportTipcAndroid.h>
+#include <trusty/tipc.h>
+
+namespace android {
+
+using android::base::unique_fd;
+
+sp<RpcSession> RpcTrustyConnectWithSessionInitializer(
+        const char* device, const char* port,
+        std::function<void(sp<RpcSession>&)> sessionInitializer) {
+    auto session = RpcSession::make(RpcTransportCtxFactoryTipcAndroid::make());
+    // using the callback to initialize the session
+    sessionInitializer(session);
+    auto request = [=] {
+        int tipcFd = tipc_connect(device, port);
+        if (tipcFd < 0) {
+            LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd;
+            return unique_fd();
+        }
+        return unique_fd(tipcFd);
+    };
+    if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
+        LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str();
+        return nullptr;
+    }
+    return session;
+}
+
+sp<IBinder> RpcTrustyConnect(const char* device, const char* port) {
+    auto session = RpcTrustyConnectWithSessionInitializer(device, port, [](auto) {});
+    return session->getRootObject();
+}
+
+} // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index c91d56c..342e4a3 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -58,13 +58,10 @@
       "name": "CtsOsTestCases",
       "options": [
         {
-          "exclude-annotation": "android.platform.test.annotations.LargeTest"
+          "include-filter": "android.os.cts.BinderTest"
         },
         {
-          "exclude-filter": "android.os.cts.BuildTest#testSdkInt"
-        },
-        {
-          "exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage"
+          "include-filter": "android.os.cts.ParcelTest"
         }
       ]
     },
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index a0ade50..5dd1f90 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -39,11 +39,10 @@
 
 static void textOutputPrinter(void* cookie, const char* txt)
 {
-    ((TextOutput*)cookie)->print(txt, strlen(txt));
+    ((std::ostream*)cookie)->write(txt, strlen(txt));
 }
 
-TextOutput& operator<<(TextOutput& to, const TypeCode& val)
-{
+std::ostream& operator<<(std::ostream& to, const TypeCode& val) {
     printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to);
     return to;
 }
@@ -61,8 +60,7 @@
     else mAlignment = 1;
 }
 
-TextOutput& operator<<(TextOutput& to, const HexDump& val)
-{
+std::ostream& operator<<(std::ostream& to, const HexDump& val) {
     printHexData(0, val.buffer(), val.size(), val.bytesPerLine(),
         val.singleLineCutoff(), val.alignment(), val.carrayStyle(),
         textOutputPrinter, (void*)&to);
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/binder/Trace.cpp
similarity index 62%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/binder/Trace.cpp
index 9fac3e8..1ebfa1a 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/binder/Trace.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,19 @@
  * limitations under the License.
  */
 
-package android.gui;
+#include <binder/Trace.h>
+#include <cutils/trace.h>
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
+namespace android {
+namespace binder {
+
+void atrace_begin(uint64_t tag, const char* name) {
+    ::atrace_begin(tag, name);
 }
+
+void atrace_end(uint64_t tag) {
+    ::atrace_end(tag);
+}
+
+} // namespace binder
+} // namespace android
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 7574c29..793795e 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,23 +100,4 @@
 #define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
 #endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
 
-#ifndef BINDER_GET_EXTENDED_ERROR
-/* struct binder_extened_error - extended error information
- * @id:      identifier for the failed operation
- * @command: command as defined by binder_driver_return_protocol
- * @param:   parameter holding a negative errno value
- *
- * Used with BINDER_GET_EXTENDED_ERROR. This extends the error information
- * returned by the driver upon a failed operation. Userspace can pull this
- * data to properly handle specific error scenarios.
- */
-struct binder_extended_error {
-    __u32 id;
-    __u32 command;
-    __s32 param;
-};
-
-#define BINDER_GET_EXTENDED_ERROR _IOWR('b', 17, struct binder_extended_error)
-#endif // BINDER_GET_EXTENDED_ERROR
-
 #endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 46223bb..08dbd13 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -59,6 +59,8 @@
     virtual void*       findObject(const void* objectID) const final;
     virtual void* detachObject(const void* objectID) final;
     void withLock(const std::function<void()>& doWithLock);
+    sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make,
+                                   const void* makeArgs);
 
     virtual BBinder*    localBinder();
 
@@ -103,6 +105,12 @@
     [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd,
                                              const sp<IBinder>& keepAliveBinder);
 
+    // Start recording transactions to the unique_fd in data.
+    // See BinderRecordReplay.h for more details.
+    [[nodiscard]] status_t startRecordingTransactions(const Parcel& data);
+    // Stop the current recording.
+    [[nodiscard]] status_t stopRecordingTransactions();
+
 protected:
     virtual             ~BBinder();
 
@@ -129,7 +137,7 @@
     friend ::android::internal::Stability;
     int16_t mStability;
     bool mParceled;
-    uint8_t mReserved0;
+    bool mRecordingOn;
 
 #ifdef __LP64__
     int32_t mReserved1;
diff --git a/libs/binder/include/binder/BinderRecordReplay.h b/libs/binder/include/binder/BinderRecordReplay.h
new file mode 100644
index 0000000..25ed5e5
--- /dev/null
+++ b/libs/binder/include/binder/BinderRecordReplay.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <mutex>
+
+namespace android {
+
+namespace binder::debug {
+
+// Warning: Transactions are sequentially recorded to the file descriptor in a
+// non-stable format. A detailed description of the recording format can be found in
+// BinderRecordReplay.cpp.
+
+class RecordedTransaction {
+public:
+    // Filled with the first transaction from fd.
+    static std::optional<RecordedTransaction> fromFile(const android::base::unique_fd& fd);
+    // Filled with the arguments.
+    static std::optional<RecordedTransaction> fromDetails(uint32_t code, uint32_t flags,
+                                                          const Parcel& data, const Parcel& reply,
+                                                          status_t err);
+    RecordedTransaction(RecordedTransaction&& t) noexcept;
+
+    [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const;
+
+    uint32_t getCode() const;
+    uint32_t getFlags() const;
+    uint64_t getDataSize() const;
+    uint64_t getReplySize() const;
+    int32_t getReturnedStatus() const;
+    uint32_t getVersion() const;
+    const Parcel& getDataParcel() const;
+    const Parcel& getReplyParcel() const;
+
+private:
+    RecordedTransaction() = default;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wpadded"
+    struct TransactionHeader {
+        uint32_t code = 0;
+        uint32_t flags = 0;
+        uint64_t dataSize = 0;
+        uint64_t replySize = 0;
+        int32_t statusReturned = 0;
+        uint32_t version = 0; // !0 iff Rpc
+    };
+#pragma clang diagnostic pop
+    static_assert(sizeof(TransactionHeader) == 32);
+    static_assert(sizeof(TransactionHeader) % 8 == 0);
+
+    TransactionHeader mHeader;
+    Parcel mSent;
+    Parcel mReply;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+    uint8_t mReserved[40];
+#pragma clang diagnostic pop
+};
+
+} // namespace binder::debug
+
+} // namespace android
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 19ad5e6..57e103d 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
 #include <utils/Mutex.h>
 
@@ -72,6 +73,8 @@
     virtual void*       findObject(const void* objectID) const final;
     virtual void* detachObject(const void* objectID) final;
     void withLock(const std::function<void()>& doWithLock);
+    sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make,
+                                   const void* makeArgs);
 
     virtual BpBinder*   remoteBinder();
 
@@ -87,6 +90,12 @@
 
     std::optional<int32_t> getDebugBinderHandle() const;
 
+    // Start recording transactions to the unique_fd.
+    // See BinderRecordReplay.h for more details.
+    status_t startRecordingBinder(const android::base::unique_fd& fd);
+    // Stop the current recording.
+    status_t stopRecordingBinder();
+
     class ObjectManager {
     public:
         ObjectManager();
@@ -96,6 +105,8 @@
                      IBinder::object_cleanup_func func);
         void* find(const void* objectID) const;
         void* detach(const void* objectID);
+        sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make,
+                                       const void* makeArgs);
 
         void kill();
 
@@ -104,9 +115,9 @@
         ObjectManager& operator=(const ObjectManager&);
 
         struct entry_t {
-            void* object;
-            void* cleanupCookie;
-            IBinder::object_cleanup_func func;
+            void* object = nullptr;
+            void* cleanupCookie = nullptr;
+            IBinder::object_cleanup_func func = nullptr;
         };
 
         std::map<const void*, entry_t> mObjects;
diff --git a/libs/binder/include/binder/Delegate.h b/libs/binder/include/binder/Delegate.h
new file mode 100644
index 0000000..8b3fc1c
--- /dev/null
+++ b/libs/binder/include/binder/Delegate.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IBinder.h>
+
+#ifndef __BIONIC__
+#ifndef __assert
+
+// defined differently by liblog
+#pragma push_macro("LOG_PRI")
+#ifdef LOG_PRI
+#undef LOG_PRI
+#endif
+#include <syslog.h>
+#pragma pop_macro("LOG_PRI")
+
+#define __assert(a, b, c)          \
+    do {                           \
+        syslog(LOG_ERR, a ": " c); \
+        abort();                   \
+    } while (false)
+#endif // __assert
+#endif // __BIONIC__
+
+namespace android {
+
+/*
+ * Used to manage AIDL's *Delegator types.
+ * This is used to:
+ * - create a new *Delegator object that delegates to the binder argument.
+ * - or return an existing *Delegator object that already delegates to the
+ * binder argument.
+ * - or return the underlying delegate binder if the binder argument is a
+ * *Delegator itself.
+ *
+ * @param binder - the binder to delegate to or unwrap
+ *
+ * @return pointer to the *Delegator object or the unwrapped binder object
+ */
+template <typename T>
+sp<T> delegate(const sp<T>& binder) {
+    const void* isDelegatorId = &T::descriptor;
+    const void* hasDelegatorId = &T::descriptor + 1;
+    // is binder itself a delegator?
+    if (T::asBinder(binder)->findObject(isDelegatorId)) {
+        if (T::asBinder(binder)->findObject(hasDelegatorId)) {
+            __assert(__FILE__, __LINE__,
+                     "This binder has a delegator and is also delegator itself! This is "
+                     "likely an unintended mixing of binders.");
+            return nullptr;
+        }
+        // unwrap the delegator
+        return static_cast<typename T::DefaultDelegator*>(binder.get())->getImpl();
+    }
+
+    struct MakeArgs {
+        const sp<T>* binder;
+        const void* id;
+    } makeArgs;
+    makeArgs.binder = &binder;
+    makeArgs.id = isDelegatorId;
+
+    // the binder is not a delegator, so construct one
+    sp<IBinder> newDelegator = T::asBinder(binder)->lookupOrCreateWeak(
+            hasDelegatorId,
+            [](const void* args) -> sp<IBinder> {
+                auto delegator = sp<typename T::DefaultDelegator>::make(
+                        *static_cast<const MakeArgs*>(args)->binder);
+                // make sure we know this binder is a delegator by attaching a unique ID
+                (void)delegator->attachObject(static_cast<const MakeArgs*>(args)->id,
+                                              reinterpret_cast<void*>(0x1), nullptr, nullptr);
+                return delegator;
+            },
+            static_cast<const void*>(&makeArgs));
+    return sp<typename T::DefaultDelegator>::cast(newDelegator);
+}
+
+} // namespace android
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 43fc5ff..e75d548 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -56,6 +56,8 @@
         LAST_CALL_TRANSACTION = 0x00ffffff,
 
         PING_TRANSACTION = B_PACK_CHARS('_', 'P', 'N', 'G'),
+        START_RECORDING_TRANSACTION = B_PACK_CHARS('_', 'S', 'R', 'D'),
+        STOP_RECORDING_TRANSACTION = B_PACK_CHARS('_', 'E', 'R', 'D'),
         DUMP_TRANSACTION = B_PACK_CHARS('_', 'D', 'M', 'P'),
         SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_', 'C', 'M', 'D'),
         INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
@@ -284,6 +286,9 @@
 
     virtual BBinder*        localBinder();
     virtual BpBinder*       remoteBinder();
+    typedef sp<IBinder> (*object_make_func)(const void* makeArgs);
+    sp<IBinder> lookupOrCreateWeak(const void* objectID, object_make_func make,
+                                   const void* makeArgs);
 
 protected:
     virtual          ~IBinder();
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5469239..6de6ce8 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -595,7 +595,7 @@
     // uid.
     uid_t               readCallingWorkSourceUid() const;
 
-    void                print(TextOutput& to, uint32_t flags = 0) const;
+    void print(std::ostream& to, uint32_t flags = 0) const;
 
 private:
     // `objects` and `objectsSize` always 0 for RPC Parcels.
@@ -1594,8 +1594,7 @@
 
 // ---------------------------------------------------------------------------
 
-inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
-{
+inline std::ostream& operator<<(std::ostream& to, const Parcel& parcel) {
     parcel.print(to);
     return to;
 }
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 52bda0e..81ae26a3 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -50,6 +50,17 @@
             std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
     /**
+     * Creates an RPC server that bootstraps sessions using an existing
+     * Unix domain socket pair.
+     *
+     * Callers should create a pair of SOCK_STREAM Unix domain sockets, pass
+     * one to RpcServer::setupUnixDomainSocketBootstrapServer and the other
+     * to RpcSession::setupUnixDomainSocketBootstrapClient. Multiple client
+     * session can be created from the client end of the pair.
+     */
+    [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(base::unique_fd serverFd);
+
+    /**
      * This represents a session for responses, e.g.:
      *
      *     process A serves binder a
@@ -187,6 +198,11 @@
     std::vector<sp<RpcSession>> listSessions();
     size_t numUninitializedSessions();
 
+    /**
+     * Whether any requests are currently being processed.
+     */
+    bool hasActiveRequests();
+
     ~RpcServer();
 
 private:
@@ -197,11 +213,18 @@
     void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
     void onSessionIncomingThreadEnded() override;
 
+    status_t setupExternalServer(
+            base::unique_fd serverFd,
+            std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn);
+
     static constexpr size_t kRpcAddressSize = 128;
     static void establishConnection(
-            sp<RpcServer>&& server, base::unique_fd clientFd,
+            sp<RpcServer>&& server, RpcTransportFd clientFd,
             std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen,
             std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn);
+    static status_t acceptSocketConnection(const RpcServer& server, RpcTransportFd* out);
+    static status_t recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out);
+
     [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address);
 
     const std::unique_ptr<RpcTransportCtx> mCtx;
@@ -210,7 +233,7 @@
     // A mode is supported if the N'th bit is on, where N is the mode enum's value.
     std::bitset<8> mSupportedFileDescriptorTransportModes = std::bitset<8>().set(
             static_cast<size_t>(RpcSession::FileDescriptorTransportMode::NONE));
-    base::unique_fd mServer; // socket we are accepting sessions on
+    RpcTransportFd mServer; // socket we are accepting sessions on
 
     RpcMutex mLock; // for below
     std::unique_ptr<RpcMaybeThread> mJoinThread;
@@ -223,6 +246,7 @@
     std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
     std::unique_ptr<FdTrigger> mShutdownTrigger;
     RpcConditionVariable mShutdownCv;
+    std::function<status_t(const RpcServer& server, RpcTransportFd* out)> mAcceptFn;
 };
 
 } // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 428e272..40faf2c 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -100,6 +100,8 @@
         NONE = 0,
         // Send file descriptors via unix domain socket ancillary data.
         UNIX = 1,
+        // Send file descriptors as Trusty IPC handles.
+        TRUSTY = 2,
     };
 
     /**
@@ -115,6 +117,11 @@
     [[nodiscard]] status_t setupUnixDomainClient(const char* path);
 
     /**
+     * Connects to an RPC server over a nameless Unix domain socket pair.
+     */
+    [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(base::unique_fd bootstrap);
+
+    /**
      * Connects to an RPC server at the CVD & port.
      */
     [[nodiscard]] status_t setupVsockClient(unsigned int cvd, unsigned int port);
@@ -189,6 +196,11 @@
      */
     [[nodiscard]] status_t sendDecStrong(const BpBinder* binder);
 
+    /**
+     * Whether any requests are currently being processed.
+     */
+    bool hasActiveRequests();
+
     ~RpcSession();
 
     /**
@@ -228,6 +240,7 @@
 
     private:
         RpcConditionVariable mCv;
+        std::atomic<size_t> mShutdownCount = 0;
     };
     friend WaitForShutdownListener;
 
@@ -269,7 +282,7 @@
     [[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address,
                                                     const std::vector<uint8_t>& sessionId,
                                                     bool incoming);
-    [[nodiscard]] status_t initAndAddConnection(base::unique_fd fd,
+    [[nodiscard]] status_t initAndAddConnection(RpcTransportFd fd,
                                                 const std::vector<uint8_t>& sessionId,
                                                 bool incoming);
     [[nodiscard]] status_t addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport);
@@ -286,6 +299,11 @@
 
     [[nodiscard]] status_t initShutdownTrigger();
 
+    /**
+     * Checks whether any connection is active (Not polling on fd)
+     */
+    bool hasActiveConnection(const std::vector<sp<RpcConnection>>& connections);
+
     enum class ConnectionUse {
         CLIENT,
         CLIENT_ASYNC,
@@ -356,11 +374,14 @@
 
     RpcConditionVariable mAvailableConnectionCv; // for mWaitingThreads
 
+    std::unique_ptr<RpcTransport> mBootstrapTransport;
+
     struct ThreadState {
         size_t mWaitingThreads = 0;
         // hint index into clients, ++ when sending an async transaction
         size_t mOutgoingOffset = 0;
         std::vector<sp<RpcConnection>> mOutgoing;
+        // max size of mIncoming. Once any thread starts down, no more can be started.
         size_t mMaxIncoming = 0;
         std::vector<sp<RpcConnection>> mIncoming;
         std::map<RpcMaybeThread::id, RpcMaybeThread> mThreads;
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 5197ef9..fd52a3a 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -30,12 +30,14 @@
 #include <utils/Errors.h>
 
 #include <binder/RpcCertificateFormat.h>
+#include <binder/RpcThreads.h>
 
 #include <sys/uio.h>
 
 namespace android {
 
 class FdTrigger;
+struct RpcTransportFd;
 
 // Represents a socket connection.
 // No thread-safety is guaranteed for these APIs.
@@ -81,6 +83,15 @@
             const std::optional<android::base::function_ref<status_t()>> &altPoll,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
 
+    /**
+     *  Check whether any threads are blocked while polling the transport
+     *  for read operations
+     *  Return:
+     *    True - Specifies that there is active polling on transport.
+     *    False - No active polling on transport
+     */
+    [[nodiscard]] virtual bool isWaiting() = 0;
+
 protected:
     RpcTransport() = default;
 };
@@ -96,7 +107,7 @@
     // Implementation details: for TLS, this function may incur I/O. |fdTrigger| may be used
     // to interrupt I/O. This function blocks until handshake is finished.
     [[nodiscard]] virtual std::unique_ptr<RpcTransport> newTransport(
-            android::base::unique_fd fd, FdTrigger *fdTrigger) const = 0;
+            android::RpcTransportFd fd, FdTrigger *fdTrigger) const = 0;
 
     // Return the preconfigured certificate of this context.
     //
@@ -129,4 +140,36 @@
     RpcTransportCtxFactory() = default;
 };
 
+struct RpcTransportFd {
+private:
+    mutable bool isPolling{false};
+
+    void setPollingState(bool state) const { isPolling = state; }
+
+public:
+    base::unique_fd fd;
+
+    RpcTransportFd() = default;
+    explicit RpcTransportFd(base::unique_fd &&descriptor)
+          : isPolling(false), fd(std::move(descriptor)) {}
+
+    RpcTransportFd(RpcTransportFd &&transportFd) noexcept
+          : isPolling(transportFd.isPolling), fd(std::move(transportFd.fd)) {}
+
+    RpcTransportFd &operator=(RpcTransportFd &&transportFd) noexcept {
+        fd = std::move(transportFd.fd);
+        isPolling = transportFd.isPolling;
+        return *this;
+    }
+
+    RpcTransportFd &operator=(base::unique_fd &&descriptor) noexcept {
+        fd = std::move(descriptor);
+        isPolling = false;
+        return *this;
+    }
+
+    bool isInPollingState() const { return isPolling; }
+    friend class FdTrigger;
+};
+
 } // namespace android
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
index bf9c92b..eb98042 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -94,7 +94,7 @@
     uint32_t mCode;
 };
 
-TextOutput& operator<<(TextOutput& to, const TypeCode& val);
+std::ostream& operator<<(std::ostream& to, const TypeCode& val);
 
 class HexDump
 {
@@ -123,7 +123,7 @@
     bool mCArrayStyle;
 };
 
-TextOutput& operator<<(TextOutput& to, const HexDump& val);
+std::ostream& operator<<(std::ostream& to, const HexDump& val);
 inline TextOutput& operator<<(TextOutput& to,
                               decltype(std::endl<char,
                                        std::char_traits<char>>)
diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h
new file mode 100644
index 0000000..9937842
--- /dev/null
+++ b/libs/binder/include/binder/Trace.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cutils/trace.h>
+#include <stdint.h>
+
+namespace android {
+namespace binder {
+
+// Trampoline functions allowing generated aidls to trace binder transactions without depending on
+// libcutils/libutils
+void atrace_begin(uint64_t tag, const char* name);
+void atrace_end(uint64_t tag);
+
+class ScopedTrace {
+public:
+    inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); }
+
+    inline ~ScopedTrace() { atrace_end(mTag); }
+
+private:
+    uint64_t mTag;
+};
+
+} // namespace binder
+} // namespace android
diff --git a/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h
new file mode 100644
index 0000000..4a4172a
--- /dev/null
+++ b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Wraps the transport layer of RPC. Implementation uses Trusty IPC.
+
+#pragma once
+
+#include <memory>
+
+#include <binder/RpcTransport.h>
+
+namespace android {
+
+// RpcTransportCtxFactory for writing Trusty IPC clients in Android.
+class RpcTransportCtxFactoryTipcAndroid : public RpcTransportCtxFactory {
+public:
+    static std::unique_ptr<RpcTransportCtxFactory> make();
+
+    std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
+    std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
+    const char* toCString() const override;
+
+private:
+    RpcTransportCtxFactoryTipcAndroid() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/include_trusty/binder/RpcTrusty.h b/libs/binder/include_trusty/binder/RpcTrusty.h
new file mode 100644
index 0000000..b034b9b
--- /dev/null
+++ b/libs/binder/include_trusty/binder/RpcTrusty.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IBinder.h>
+
+namespace android {
+
+sp<IBinder> RpcTrustyConnect(const char* device, const char* port);
+
+sp<RpcSession> RpcTrustyConnectWithSessionInitializer(
+        const char* device, const char* port,
+        std::function<void(sp<RpcSession>&)> sessionInitializer);
+
+} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 32e018d..8ae7537 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -57,6 +57,7 @@
     srcs: [
         "ibinder.cpp",
         "ibinder_jni.cpp",
+        "libbinder.cpp",
         "parcel.cpp",
         "parcel_jni.cpp",
         "process.cpp",
@@ -181,4 +182,8 @@
     name: "libbinder_ndk",
     symbol_file: "libbinder_ndk.map.txt",
     first_version: "29",
+    export_header_libs: [
+        "libbinder_ndk_headers",
+        "libbinder_ndk_helper_headers",
+    ],
 }
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 9778ec0..28d1f16 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,21 +14,19 @@
  * limitations under the License.
  */
 
+#include <android-base/logging.h>
 #include <android/binder_ibinder.h>
 #include <android/binder_ibinder_platform.h>
-#include <android/binder_libbinder.h>
-#include "ibinder_internal.h"
-
 #include <android/binder_stability.h>
 #include <android/binder_status.h>
-#include "parcel_internal.h"
-#include "status_internal.h"
-
-#include <android-base/logging.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IResultReceiver.h>
 #include <private/android_filesystem_config.h>
 
+#include "ibinder_internal.h"
+#include "parcel_internal.h"
+#include "status_internal.h"
+
 using DeathRecipient = ::android::IBinder::DeathRecipient;
 
 using ::android::IBinder;
@@ -782,17 +780,6 @@
     return ::android::IPCThreadState::self()->getCallingSid();
 }
 
-android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder) {
-    if (binder == nullptr) return nullptr;
-    return binder->getBinder();
-}
-
-AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder) {
-    sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder);
-    AIBinder_incStrong(ndkBinder.get());
-    return ndkBinder.get();
-}
-
 void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) {
     binder->asABBinder()->setMinSchedulerPolicy(policy, priority);
 }
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 7ea9be7..fccc0af 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -349,7 +349,7 @@
     /**
      * See AIBinder_Weak_promote.
      */
-    SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); }
+    SpAIBinder promote() const { return SpAIBinder(AIBinder_Weak_promote(get())); }
 };
 
 namespace internal {
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 0bf1e3d..95eee26 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -1639,7 +1639,6 @@
         return AParcel_writeParcelable(parcel, value);
     } else {
         static_assert(dependent_false_v<T>, "unrecognized type");
-        return STATUS_OK;
     }
 }
 
@@ -1707,7 +1706,6 @@
         return AParcel_readParcelable(parcel, value);
     } else {
         static_assert(dependent_false_v<T>, "unrecognized type");
-        return STATUS_OK;
     }
 }
 
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index f45aa76..c1f2620 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -58,6 +58,9 @@
 #endif
             AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
                                AParcel_getDataSize(other.mParcel.get()));
+        } else {
+            syslog(LOG_ERR,
+                   "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
         }
     }
 #endif
@@ -192,6 +195,9 @@
         if (__ANDROID_API__ >= 31) {
 #endif
             AParcel_reset(mParcel.get());
+        } else {
+            syslog(LOG_ERR,
+                   "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
         }
     }
 
@@ -201,6 +207,29 @@
     inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; }
     inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; }
     inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; }
+#if __ANDROID_API__ >= 31
+    inline AParcelableHolder& operator=(const AParcelableHolder& rhs) {
+        // AParcelableHolder has been introduced in 31.
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
+        if (__builtin_available(android 31, *)) {
+#else
+        if (__ANDROID_API__ >= 31) {
+#endif
+            this->reset();
+            if (this->mStability != rhs.mStability) {
+                syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!",
+                       this->mStability, rhs.mStability);
+                abort();
+            }
+            AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0,
+                               AParcel_getDataSize(rhs.mParcel.get()));
+        } else {
+            syslog(LOG_ERR,
+                   "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
+        }
+        return *this;
+    }
+#endif
 
    private:
     mutable ndk::ScopedAParcel mParcel;
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index ef71a81..d7840ec 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -162,7 +162,12 @@
     } else if constexpr (std::is_same_v<bool, _T>) {
         return t ? "true" : "false";
     } else if constexpr (std::is_same_v<char16_t, _T>) {
+        // TODO(b/244494451): codecvt is deprecated in C++17 -- suppress the
+        // warnings. There's no replacement in the standard library yet.
+        _Pragma("clang diagnostic push")
+                _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"");
         return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(t);
+        _Pragma("clang diagnostic pop");
     } else if constexpr (std::is_arithmetic_v<_T>) {
         return std::to_string(t);
     } else if constexpr (std::is_same_v<std::string, _T>) {
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
index f0c00e8..74a7157 100644
--- a/libs/binder/ndk/include_platform/android/binder_libbinder.h
+++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h
@@ -16,10 +16,12 @@
 
 #pragma once
 
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)) || defined(__TRUSTY__)
 
 #include <android/binder_ibinder.h>
+#include <android/binder_parcel.h>
 #include <binder/IBinder.h>
+#include <binder/Parcel.h>
 
 /**
  * Get libbinder version of binder from AIBinder.
@@ -47,4 +49,26 @@
  */
 AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
 
+/**
+ * View libbinder version of parcel from AParcel (mutable).
+ *
+ * The lifetime of the returned parcel is the lifetime of the input AParcel.
+ * Do not ues this reference after dropping the AParcel.
+ *
+ * \param parcel non-null parcel with ownership retained by client
+ * \return platform parcel object
+ */
+android::Parcel* AParcel_viewPlatformParcel(AParcel* parcel);
+
+/**
+ * View libbinder version of parcel from AParcel (const version).
+ *
+ * The lifetime of the returned parcel is the lifetime of the input AParcel.
+ * Do not ues this reference after dropping the AParcel.
+ *
+ * \param parcel non-null parcel with ownership retained by client
+ * \return platform parcel object
+ */
+const android::Parcel* AParcel_viewPlatformParcel(const AParcel* parcel);
+
 #endif
diff --git a/libs/binder/ndk/libbinder.cpp b/libs/binder/ndk/libbinder.cpp
new file mode 100644
index 0000000..f94d81d
--- /dev/null
+++ b/libs/binder/ndk/libbinder.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_libbinder.h>
+
+#include "ibinder_internal.h"
+#include "parcel_internal.h"
+
+using ::android::IBinder;
+using ::android::Parcel;
+using ::android::sp;
+
+sp<IBinder> AIBinder_toPlatformBinder(AIBinder* binder) {
+    if (binder == nullptr) return nullptr;
+    return binder->getBinder();
+}
+
+AIBinder* AIBinder_fromPlatformBinder(const sp<IBinder>& binder) {
+    sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder);
+    AIBinder_incStrong(ndkBinder.get());
+    return ndkBinder.get();
+}
+
+Parcel* AParcel_viewPlatformParcel(AParcel* parcel) {
+    return parcel->get();
+}
+
+const Parcel* AParcel_viewPlatformParcel(const AParcel* parcel) {
+    return parcel->get();
+}
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 6bc9814..259a736 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -158,6 +158,7 @@
     extern "C++" {
         AIBinder_fromPlatformBinder*;
         AIBinder_toPlatformBinder*;
+        AParcel_viewPlatformParcel*;
     };
   local:
     *;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 1b136dc..01b9472 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -661,6 +661,35 @@
     }
 }
 
+TEST(NdkBinder, ConvertToPlatformParcel) {
+    ndk::ScopedAParcel parcel = ndk::ScopedAParcel(AParcel_create());
+    EXPECT_EQ(OK, AParcel_writeInt32(parcel.get(), 42));
+
+    android::Parcel* pparcel = AParcel_viewPlatformParcel(parcel.get());
+    pparcel->setDataPosition(0);
+    EXPECT_EQ(42, pparcel->readInt32());
+}
+
+TEST(NdkBinder, GetAndVerifyScopedAIBinder_Weak) {
+    for (const ndk::SpAIBinder& binder :
+         {// remote
+          ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)),
+          // local
+          ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
+        // get a const ScopedAIBinder_Weak and verify promote
+        EXPECT_NE(binder.get(), nullptr);
+        const ndk::ScopedAIBinder_Weak wkAIBinder =
+                ndk::ScopedAIBinder_Weak(AIBinder_Weak_new(binder.get()));
+        EXPECT_EQ(wkAIBinder.promote().get(), binder.get());
+        // get another ScopedAIBinder_Weak and verify
+        ndk::ScopedAIBinder_Weak wkAIBinder2 =
+                ndk::ScopedAIBinder_Weak(AIBinder_Weak_new(binder.get()));
+        EXPECT_FALSE(AIBinder_Weak_lt(wkAIBinder.get(), wkAIBinder2.get()));
+        EXPECT_FALSE(AIBinder_Weak_lt(wkAIBinder2.get(), wkAIBinder.get()));
+        EXPECT_EQ(wkAIBinder2.promote(), wkAIBinder.promote());
+    }
+}
+
 class MyResultReceiver : public BnResultReceiver {
    public:
     Mutex mMutex;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index c0d4487..a135796 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -30,6 +30,7 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.rkpd",
         "com.android.uwb",
         "com.android.virt",
     ],
@@ -80,6 +81,7 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.rkpd",
         "com.android.uwb",
         "com.android.virt",
     ],
@@ -138,24 +140,7 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
-        "com.android.uwb",
-        "com.android.virt",
-    ],
-    min_sdk_version: "Tiramisu",
-}
-
-// TODO(b/184872979): remove once the Rust API is created.
-rust_bindgen {
-    name: "libbinder_rpc_unstable_bindgen",
-    wrapper_src: ":libbinder_rpc_unstable_header",
-    crate_name: "binder_rpc_unstable_bindgen",
-    visibility: ["//packages/modules/Virtualization:__subpackages__"],
-    source_stem: "bindings",
-    shared_libs: [
-        "libutils",
-    ],
-    apex_available: [
-        "com.android.compos",
+        "com.android.rkpd",
         "com.android.uwb",
         "com.android.virt",
     ],
@@ -188,13 +173,3 @@
     clippy_lints: "none",
     lints: "none",
 }
-
-rust_test {
-    name: "libbinder_rpc_unstable_bindgen_test",
-    srcs: [":libbinder_rpc_unstable_bindgen"],
-    crate_name: "binder_rpc_unstable_bindgen",
-    test_suites: ["general-tests"],
-    auto_gen_config: true,
-    clippy_lints: "none",
-    lints: "none",
-}
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
new file mode 100644
index 0000000..5ebc27f
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -0,0 +1,100 @@
+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"],
+}
+
+rust_library {
+    name: "librpcbinder_rs",
+    crate_name: "rpcbinder",
+    srcs: ["src/lib.rs"],
+    shared_libs: [
+        "libutils",
+    ],
+    rustlibs: [
+        "libbinder_ndk_sys",
+        "libbinder_rpc_unstable_bindgen_sys",
+        "libbinder_rs",
+        "libdowncast_rs",
+        "liblibc",
+    ],
+    apex_available: [
+        "com.android.compos",
+        "com.android.uwb",
+        "com.android.virt",
+    ],
+    min_sdk_version: "Tiramisu",
+}
+
+// Build a separate rust_library rather than depending directly on libbinder_rpc_unstable_bindgen,
+// to work around the fact that rust_bindgen targets only produce rlibs and not dylibs, which would
+// result in duplicate conflicting versions of libbinder_ndk_sys. This will hopefully be fixed in
+// the build system, at which point we can delete this target and go back to using
+// libbinder_rpc_unstable_bindgen directly.
+rust_library {
+    name: "libbinder_rpc_unstable_bindgen_sys",
+    crate_name: "binder_rpc_unstable_bindgen",
+    srcs: [
+        ":libbinder_rpc_unstable_bindgen",
+    ],
+    visibility: [":__subpackages__"],
+    rustlibs: [
+        "libbinder_ndk_sys",
+    ],
+    shared_libs: [
+        "libbinder_rpc_unstable",
+        "libutils",
+    ],
+    apex_available: [
+        "com.android.compos",
+        "com.android.uwb",
+        "com.android.virt",
+    ],
+    min_sdk_version: "Tiramisu",
+    lints: "none",
+    clippy_lints: "none",
+}
+
+// TODO(b/184872979): remove once the RPC Binder API is stabilised.
+rust_bindgen {
+    name: "libbinder_rpc_unstable_bindgen",
+    wrapper_src: ":libbinder_rpc_unstable_header",
+    crate_name: "binder_rpc_unstable_bindgen",
+    visibility: [":__subpackages__"],
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--blocklist-type",
+        "AIBinder",
+        "--raw-line",
+        "use binder_ndk_sys::AIBinder;",
+    ],
+    rustlibs: [
+        "libbinder_ndk_sys",
+    ],
+    shared_libs: [
+        "libbinder_rpc_unstable",
+        "libutils",
+    ],
+    apex_available: [
+        "com.android.compos",
+        "com.android.uwb",
+        "com.android.virt",
+    ],
+    min_sdk_version: "Tiramisu",
+}
+
+rust_test {
+    name: "libbinder_rpc_unstable_bindgen_test",
+    srcs: [":libbinder_rpc_unstable_bindgen"],
+    crate_name: "binder_rpc_unstable_bindgen",
+    rustlibs: [
+        "libbinder_ndk_sys",
+    ],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    clippy_lints: "none",
+    lints: "none",
+}
diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs
new file mode 100644
index 0000000..743800b
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/src/client.rs
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use binder::{unstable_api::new_spibinder, FromIBinder, SpIBinder, StatusCode, Strong};
+use std::os::{
+    raw::{c_int, c_void},
+    unix::io::RawFd,
+};
+
+/// Connects to an RPC Binder server over vsock.
+pub fn get_vsock_rpc_service(cid: u32, port: u32) -> Option<SpIBinder> {
+    // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can
+    // safely be taken by new_spibinder.
+    unsafe { new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, port)) }
+}
+
+/// Connects to an RPC Binder server for a particular interface over vsock.
+pub fn get_vsock_rpc_interface<T: FromIBinder + ?Sized>(
+    cid: u32,
+    port: u32,
+) -> Result<Strong<T>, StatusCode> {
+    interface_cast(get_vsock_rpc_service(cid, port))
+}
+
+/// Connects to an RPC Binder server, using the given callback to get (and take ownership of)
+/// file descriptors already connected to it.
+pub fn get_preconnected_rpc_service(
+    mut request_fd: impl FnMut() -> Option<RawFd>,
+) -> Option<SpIBinder> {
+    // Double reference the factory because trait objects aren't FFI safe.
+    let mut request_fd_ref: RequestFd = &mut request_fd;
+    let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
+
+    // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
+    // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
+    // of param, only passing it to request_fd_wrapper.
+    unsafe {
+        new_spibinder(binder_rpc_unstable_bindgen::RpcPreconnectedClient(
+            Some(request_fd_wrapper),
+            param,
+        ))
+    }
+}
+
+type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
+
+unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
+    // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
+    // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
+    // initialized instance.
+    let request_fd_ptr = param as *mut RequestFd;
+    let request_fd = request_fd_ptr.as_mut().unwrap();
+    if let Some(fd) = request_fd() {
+        fd
+    } else {
+        -1
+    }
+}
+
+/// Connects to an RPC Binder server for a particular interface, using the given callback to get
+/// (and take ownership of) file descriptors already connected to it.
+pub fn get_preconnected_rpc_interface<T: FromIBinder + ?Sized>(
+    request_fd: impl FnMut() -> Option<RawFd>,
+) -> Result<Strong<T>, StatusCode> {
+    interface_cast(get_preconnected_rpc_service(request_fd))
+}
+
+fn interface_cast<T: FromIBinder + ?Sized>(
+    service: Option<SpIBinder>,
+) -> Result<Strong<T>, StatusCode> {
+    if let Some(service) = service {
+        FromIBinder::try_from(service)
+    } else {
+        Err(StatusCode::NAME_NOT_FOUND)
+    }
+}
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/binder/rust/rpcbinder/src/lib.rs
similarity index 63%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/binder/rust/rpcbinder/src/lib.rs
index 9fac3e8..a5eea61 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package android.gui;
+//! API for RPC Binder services.
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
-}
+mod client;
+mod server;
+
+pub use client::{
+    get_preconnected_rpc_interface, get_preconnected_rpc_service, get_vsock_rpc_interface,
+    get_vsock_rpc_service,
+};
+pub use server::{run_rpc_server, run_rpc_server_with_factory};
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
new file mode 100644
index 0000000..aeb23c6
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use binder::{
+    unstable_api::{AIBinder, AsNative},
+    SpIBinder,
+};
+use std::{os::raw, ptr::null_mut};
+
+/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
+/// port.
+///
+/// If and when the server is ready for connections (it is listening on the port), `on_ready` is
+/// called to allow appropriate action to be taken - e.g. to notify clients that they may now
+/// attempt to connect.
+///
+/// The current thread is joined to the binder thread pool to handle incoming messages.
+///
+/// Returns true if the server has shutdown normally, false if it failed in some way.
+pub fn run_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool
+where
+    F: FnOnce(),
+{
+    let mut ready_notifier = ReadyNotifier(Some(on_ready));
+    ready_notifier.run_server(service, port)
+}
+
+struct ReadyNotifier<F>(Option<F>)
+where
+    F: FnOnce();
+
+impl<F> ReadyNotifier<F>
+where
+    F: FnOnce(),
+{
+    fn run_server(&mut self, mut service: SpIBinder, port: u32) -> bool {
+        let service = service.as_native_mut();
+        let param = self.as_void_ptr();
+
+        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+        // Plus the binder objects are threadsafe.
+        // RunRpcServerCallback does not retain a reference to `ready_callback` or `param`; it only
+        // uses them before it returns, which is during the lifetime of `self`.
+        unsafe {
+            binder_rpc_unstable_bindgen::RunRpcServerCallback(
+                service,
+                port,
+                Some(Self::ready_callback),
+                param,
+            )
+        }
+    }
+
+    fn as_void_ptr(&mut self) -> *mut raw::c_void {
+        self as *mut _ as *mut raw::c_void
+    }
+
+    unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
+        // SAFETY: This is only ever called by `RunRpcServerCallback`, within the lifetime of the
+        // `ReadyNotifier`, with `param` taking the value returned by `as_void_ptr` (so a properly
+        // aligned non-null pointer to an initialized instance).
+        let ready_notifier = param as *mut Self;
+        ready_notifier.as_mut().unwrap().notify()
+    }
+
+    fn notify(&mut self) {
+        if let Some(on_ready) = self.0.take() {
+            on_ready();
+        }
+    }
+}
+
+type RpcServerFactoryRef<'a> = &'a mut (dyn FnMut(u32) -> Option<SpIBinder> + Send + Sync);
+
+/// Runs a binder RPC server, using the given factory function to construct a binder service
+/// implementation for each connection.
+///
+/// The current thread is joined to the binder thread pool to handle incoming messages.
+///
+/// Returns true if the server has shutdown normally, false if it failed in some way.
+pub fn run_rpc_server_with_factory(
+    port: u32,
+    mut factory: impl FnMut(u32) -> Option<SpIBinder> + Send + Sync,
+) -> bool {
+    // Double reference the factory because trait objects aren't FFI safe.
+    // NB: The type annotation is necessary to ensure that we have a `dyn` rather than an `impl`.
+    let mut factory_ref: RpcServerFactoryRef = &mut factory;
+    let context = &mut factory_ref as *mut RpcServerFactoryRef as *mut raw::c_void;
+
+    // SAFETY: `factory_wrapper` is only ever called by `RunRpcServerWithFactory`, with context
+    // taking the pointer value above (so a properly aligned non-null pointer to an initialized
+    // `RpcServerFactoryRef`), within the lifetime of `factory_ref` (i.e. no more calls will be made
+    // after `RunRpcServerWithFactory` returns).
+    unsafe {
+        binder_rpc_unstable_bindgen::RunRpcServerWithFactory(Some(factory_wrapper), context, port)
+    }
+}
+
+unsafe extern "C" fn factory_wrapper(cid: u32, context: *mut raw::c_void) -> *mut AIBinder {
+    // SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by
+    // `run_rpc_server_with_factory`, and we are still within the lifetime of the value it is
+    // pointing to.
+    let factory_ptr = context as *mut RpcServerFactoryRef;
+    let factory = factory_ptr.as_mut().unwrap();
+
+    if let Some(mut service) = factory(cid) {
+        service.as_native_mut()
+    } else {
+        null_mut()
+    }
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 63c8684..976f54d 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1091,7 +1091,7 @@
         }
     } => {
         $( #[$attr] )*
-        #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
+        #[derive(Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
         #[allow(missing_docs)]
         pub struct $enum(pub $backing);
         impl $enum {
@@ -1104,6 +1104,15 @@
             }
         }
 
+        impl std::fmt::Debug for $enum {
+            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                match self.0 {
+                    $($value => f.write_str(stringify!($name)),)*
+                    _ => f.write_fmt(format_args!("{}", self.0))
+                }
+            }
+        }
+
         impl $crate::binder_impl::Serialize for $enum {
             fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&self.0)
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 3a6dadd..dee05d0 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -296,7 +296,7 @@
     /// Must be called with a valid pointer to a `T` object. After this call,
     /// the pointer will be invalid and should not be dereferenced.
     unsafe extern "C" fn on_destroy(object: *mut c_void) {
-        Box::from_raw(object as *mut T);
+        drop(Box::from_raw(object as *mut T));
     }
 
     /// Called whenever a new, local `AIBinder` object is needed of a specific
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index d7c6d49..92d132f 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -341,6 +341,11 @@
         "binderRpcTest_shared_defaults",
         "libbinder_tls_shared_deps",
     ],
+
+    // Add the Trusty mock library as a fake dependency so it gets built
+    required: [
+        "libbinder_on_trusty_mock",
+    ],
 }
 
 cc_test {
@@ -600,6 +605,7 @@
     shared_libs: [
         "libbinder",
         "liblog",
+        "libcutils",
         "libutils",
         "libutilscallstack",
         "libbase",
@@ -684,3 +690,37 @@
     ],
     test_suites: ["general-tests"],
 }
+
+cc_defaults {
+    name: "service_fuzzer_defaults",
+    static_libs: [
+        "libbase",
+        "libbinder_random_parcel",
+        "libcutils",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "libbinder_ndk",
+                "libbinder",
+                "libutils",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libbinder_ndk",
+                "libbinder",
+                "libutils",
+            ],
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+    fuzz_config: {
+        cc: [
+            "smoreland@google.com",
+            "waghpawan@google.com",
+        ],
+    },
+}
diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl
index 34d74be..4cdeac4 100644
--- a/libs/binder/tests/BinderRpcTestServerConfig.aidl
+++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl
@@ -21,5 +21,6 @@
     int rpcSecurity;
     int serverVersion;
     int vsockPort;
+    int unixBootstrapFd; // Inherited from parent
     @utf8InCpp String addr;
 }
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index b15a225..a3ed571 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -71,4 +71,13 @@
     ParcelFileDescriptor echoAsFile(@utf8InCpp String content);
 
     ParcelFileDescriptor concatFiles(in List<ParcelFileDescriptor> files);
+
+    // FDs sent via `blockingSendFdOneway` can be received via
+    // `blockingRecvFd`. The handler for `blockingSendFdOneway` will block
+    // until the next `blockingRecvFd` call.
+    //
+    // This is useful for carefully controlling how/when oneway transactions
+    // get queued.
+    oneway void blockingSendFdOneway(in ParcelFileDescriptor fd);
+    ParcelFileDescriptor blockingRecvFd();
 }
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index 60b3c94..55a3916 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -20,6 +20,7 @@
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
+#include <cutils/trace.h>
 #include <gtest/gtest.h>
 #include <utils/CallStack.h>
 
@@ -27,6 +28,8 @@
 #include <functional>
 #include <vector>
 
+static android::String8 gEmpty(""); // make sure first allocation from optimization runs
+
 struct DestructionAction {
     DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
     ~DestructionAction() { mF(); };
@@ -221,5 +224,10 @@
         return 1;
     }
     ::testing::InitGoogleTest(&argc, argv);
+
+    // if tracing is enabled, take in one-time cost
+    (void)ATRACE_INIT();
+    (void)ATRACE_GET_ENABLED_TAGS();
+
     return RUN_ALL_TESTS();
 }
diff --git a/libs/binder/tests/binderBinderUnitTest.cpp b/libs/binder/tests/binderBinderUnitTest.cpp
index ce2770f..b6aed0d 100644
--- a/libs/binder/tests/binderBinderUnitTest.cpp
+++ b/libs/binder/tests/binderBinderUnitTest.cpp
@@ -15,10 +15,11 @@
  */
 
 #include <binder/Binder.h>
-#include <binder/IBinder.h>
+#include <binder/IInterface.h>
 #include <gtest/gtest.h>
 
 using android::BBinder;
+using android::IBinder;
 using android::OK;
 using android::sp;
 
@@ -48,3 +49,49 @@
     binder->setExtension(ext);
     EXPECT_EQ(ext, binder->getExtension());
 }
+
+struct MyCookie {
+    bool* deleted;
+};
+
+class UniqueBinder : public BBinder {
+public:
+    UniqueBinder(const void* c) : cookie(reinterpret_cast<const MyCookie*>(c)) {
+        *cookie->deleted = false;
+    }
+    ~UniqueBinder() { *cookie->deleted = true; }
+    const MyCookie* cookie;
+};
+
+static sp<IBinder> make(const void* arg) {
+    return sp<UniqueBinder>::make(arg);
+}
+
+TEST(Binder, LookupOrCreateWeak) {
+    auto binder = sp<BBinder>::make();
+    bool deleted;
+    MyCookie cookie = {&deleted};
+    sp<IBinder> createdBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie);
+    EXPECT_NE(binder, createdBinder);
+
+    sp<IBinder> lookedUpBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie);
+    EXPECT_EQ(createdBinder, lookedUpBinder);
+    EXPECT_FALSE(deleted);
+}
+
+TEST(Binder, LookupOrCreateWeakDropSp) {
+    auto binder = sp<BBinder>::make();
+    bool deleted1 = false;
+    bool deleted2 = false;
+    MyCookie cookie1 = {&deleted1};
+    MyCookie cookie2 = {&deleted2};
+    sp<IBinder> createdBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie1);
+    EXPECT_NE(binder, createdBinder);
+
+    createdBinder.clear();
+    EXPECT_TRUE(deleted1);
+
+    sp<IBinder> lookedUpBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie2);
+    EXPECT_EQ(&cookie2, sp<UniqueBinder>::cast(lookedUpBinder)->cookie);
+    EXPECT_FALSE(deleted2);
+}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index e72f39c..25b524f 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -115,6 +115,7 @@
     BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
     BINDER_LIB_TEST_GETPID,
     BINDER_LIB_TEST_ECHO_VECTOR,
+    BINDER_LIB_TEST_GET_NON_BLOCKING_FD,
     BINDER_LIB_TEST_REJECT_OBJECTS,
     BINDER_LIB_TEST_CAN_GET_SID,
     BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
@@ -1158,6 +1159,56 @@
     EXPECT_EQ(readValue, testValue);
 }
 
+TEST_F(BinderLibTest, FileDescriptorRemainsNonBlocking) {
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+
+    Parcel reply;
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_NON_BLOCKING_FD, {} /*data*/, &reply),
+                StatusEq(NO_ERROR));
+    base::unique_fd fd;
+    EXPECT_THAT(reply.readUniqueFileDescriptor(&fd), StatusEq(OK));
+
+    const int result = fcntl(fd.get(), F_GETFL);
+    ASSERT_NE(result, -1);
+    EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
+// see ProcessState.cpp BINDER_VM_SIZE = 1MB.
+// This value is not exposed, but some code in the framework relies on being able to use
+// buffers near the cap size.
+constexpr size_t kSizeBytesAlmostFull = 950'000;
+constexpr size_t kSizeBytesOverFull = 1'050'000;
+
+TEST_F(BinderLibTest, GargantuanVectorSent) {
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+
+    for (size_t i = 0; i < 10; i++) {
+        // a slight variation in size is used to consider certain possible caching implementations
+        const std::vector<uint64_t> testValue((kSizeBytesAlmostFull + i) / sizeof(uint64_t), 42);
+
+        Parcel data, reply;
+        data.writeUint64Vector(testValue);
+        EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), StatusEq(NO_ERROR))
+                << i;
+        std::vector<uint64_t> readValue;
+        EXPECT_THAT(reply.readUint64Vector(&readValue), StatusEq(OK));
+        EXPECT_EQ(readValue, testValue);
+    }
+}
+
+TEST_F(BinderLibTest, LimitExceededVectorSent) {
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+    const std::vector<uint64_t> testValue(kSizeBytesOverFull / sizeof(uint64_t), 42);
+
+    Parcel data, reply;
+    data.writeUint64Vector(testValue);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply),
+                StatusEq(FAILED_TRANSACTION));
+}
+
 TEST_F(BinderLibTest, BufRejected) {
     Parcel data, reply;
     uint32_t buf;
@@ -1766,6 +1817,28 @@
                 reply->writeUint64Vector(vector);
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_GET_NON_BLOCKING_FD: {
+                std::array<int, 2> sockets;
+                const bool created = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets.data()) == 0;
+                if (!created) {
+                    ALOGE("Could not create socket pair");
+                    return UNKNOWN_ERROR;
+                }
+
+                const int result = fcntl(sockets[0], F_SETFL, O_NONBLOCK);
+                if (result != 0) {
+                    ALOGE("Could not make socket non-blocking: %s", strerror(errno));
+                    return UNKNOWN_ERROR;
+                }
+                base::unique_fd out(sockets[0]);
+                status_t writeResult = reply->writeUniqueFileDescriptor(out);
+                if (writeResult != NO_ERROR) {
+                    ALOGE("Could not write unique_fd");
+                    return writeResult;
+                }
+                close(sockets[1]); // we don't need the other side of the fd
+                return NO_ERROR;
+            }
             case BINDER_LIB_TEST_REJECT_OBJECTS: {
                 return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
             }
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 501a604..7294305 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -54,27 +54,6 @@
     EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "format must be set before data is written");
 }
 
-class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> {
-public:
-    static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) {
-        return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" +
-                std::to_string(std::get<1>(info.param));
-    }
-};
-
-TEST_P(BinderRpcServerOnly, SetExternalServerTest) {
-    base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
-    int sinkFd = sink.get();
-    auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
-    server->setProtocolVersion(std::get<1>(GetParam()));
-    ASSERT_FALSE(server->hasServer());
-    ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
-    ASSERT_TRUE(server->hasServer());
-    base::unique_fd retrieved = server->releaseServer();
-    ASSERT_FALSE(server->hasServer());
-    ASSERT_EQ(sinkFd, retrieved.get());
-}
-
 TEST(BinderRpc, CannotUseNextWireVersion) {
     auto session = RpcSession::make();
     EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
@@ -106,6 +85,11 @@
     return base::StringPrintf("unexpected state %d", wstatus);
 }
 
+static void debugBacktrace(pid_t pid) {
+    std::cerr << "TAKING BACKTRACE FOR PID " << pid << std::endl;
+    system((std::string("debuggerd -b ") + std::to_string(pid)).c_str());
+}
+
 class Process {
 public:
     Process(Process&&) = default;
@@ -146,6 +130,8 @@
     // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead.
     void terminate() { kill(mPid, SIGTERM); }
 
+    pid_t getPid() { return mPid; }
+
 private:
     std::function<void(int wstatus)> mCustomExitStatusCheck;
     pid_t mPid = 0;
@@ -194,7 +180,21 @@
 
             wp<RpcSession> weakSession = session;
             session = nullptr;
-            EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session";
+
+            // b/244325464 - 'getStrongCount' is printing '1' on failure here, which indicates the
+            // the object should not actually be promotable. By looping, we distinguish a race here
+            // from a bug causing the object to not be promotable.
+            for (size_t i = 0; i < 3; i++) {
+                sp<RpcSession> strongSession = weakSession.promote();
+                EXPECT_EQ(nullptr, strongSession)
+                        << (debugBacktrace(host.getPid()), debugBacktrace(getpid()),
+                            "Leaked sess: ")
+                        << strongSession->getStrongCount() << " checked time " << i;
+
+                if (strongSession != nullptr) {
+                    sleep(1);
+                }
+            }
         }
     }
 };
@@ -254,6 +254,25 @@
     return serverFd;
 }
 
+static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) {
+    base::unique_fd sockClient, sockServer;
+    if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) {
+        int savedErrno = errno;
+        LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+    }
+
+    int zero = 0;
+    iovec iov{&zero, sizeof(zero)};
+    std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+    fds.emplace_back(std::move(sockServer));
+
+    if (sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) {
+        int savedErrno = errno;
+        LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno);
+    }
+    return std::move(sockClient);
+}
+
 using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd,
                               android::base::borrowed_fd readEnd);
 
@@ -264,13 +283,24 @@
     RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
     uint32_t clientVersion() const { return std::get<2>(GetParam()); }
     uint32_t serverVersion() const { return std::get<3>(GetParam()); }
-    bool singleThreaded() const { return std::get<4>(GetParam()); }
+    bool serverSingleThreaded() const { return std::get<4>(GetParam()); }
     bool noKernel() const { return std::get<5>(GetParam()); }
 
+    bool clientOrServerSingleThreaded() const {
+        return !kEnableRpcThreads || serverSingleThreaded();
+    }
+
     // Whether the test params support sending FDs in parcels.
     bool supportsFdTransport() const {
         return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
-                (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX);
+                (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
+                 socketType() == SocketType::UNIX_BOOTSTRAP);
+    }
+
+    void SetUp() override {
+        if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) {
+            GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport";
+        }
     }
 
     static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
@@ -304,6 +334,14 @@
                                             singleThreaded ? "_single_threaded" : "",
                                             noKernel ? "_no_kernel" : "");
 
+        base::unique_fd bootstrapClientFd, bootstrapServerFd;
+        // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec.
+        // This is because we cannot pass ParcelFileDescriptor over a pipe.
+        if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &bootstrapServerFd)) {
+            int savedErrno = errno;
+            LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+        }
+
         auto ret = ProcessSession{
                 .host = Process([=](android::base::borrowed_fd writeEnd,
                                     android::base::borrowed_fd readEnd) {
@@ -321,6 +359,7 @@
         serverConfig.serverVersion = serverVersion;
         serverConfig.vsockPort = allocateVsockPort();
         serverConfig.addr = allocateSocketAddress();
+        serverConfig.unixBootstrapFd = bootstrapServerFd.get();
         for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
             serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
                     static_cast<int32_t>(mode));
@@ -370,6 +409,10 @@
                 case SocketType::UNIX:
                     status = session->setupUnixDomainClient(serverConfig.addr.c_str());
                     break;
+                case SocketType::UNIX_BOOTSTRAP:
+                    status = session->setupUnixDomainSocketBootstrapClient(
+                            base::unique_fd(dup(bootstrapClientFd.get())));
+                    break;
                 case SocketType::VSOCK:
                     status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
                     break;
@@ -404,18 +447,6 @@
                                      size_t sleepMs = 500);
 };
 
-// Test fixture for tests that start multiple threads.
-// This includes tests with one thread but multiple sessions,
-// since a server uses one thread per session.
-class BinderRpcThreads : public BinderRpc {
-public:
-    void SetUp() override {
-        if constexpr (!kEnableRpcThreads) {
-            GTEST_SKIP() << "Test skipped because threads were disabled at build time";
-        }
-    }
-};
-
 TEST_P(BinderRpc, Ping) {
     auto proc = createRpcTestSocketServerProcess({});
     ASSERT_NE(proc.rootBinder, nullptr);
@@ -428,7 +459,13 @@
     EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
 }
 
-TEST_P(BinderRpcThreads, MultipleSessions) {
+TEST_P(BinderRpc, MultipleSessions) {
+    if (serverSingleThreaded()) {
+        // Tests with multiple sessions require a multi-threaded service,
+        // but work fine on a single-threaded client
+        GTEST_SKIP() << "This test requires a multi-threaded service";
+    }
+
     auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
     for (auto session : proc.proc.sessions) {
         ASSERT_NE(nullptr, session.root);
@@ -436,9 +473,14 @@
     }
 }
 
-TEST_P(BinderRpcThreads, SeparateRootObject) {
+TEST_P(BinderRpc, SeparateRootObject) {
+    if (serverSingleThreaded()) {
+        GTEST_SKIP() << "This test requires a multi-threaded service";
+    }
+
     SocketType type = std::get<0>(GetParam());
-    if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) {
+    if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
+        type == SocketType::UNIX_BOOTSTRAP) {
         // we can't get port numbers for unix sockets
         return;
     }
@@ -619,7 +661,11 @@
               proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
 }
 
-TEST_P(BinderRpcThreads, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+    if (serverSingleThreaded()) {
+        GTEST_SKIP() << "This test requires a multi-threaded service";
+    }
+
     auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
 
     sp<IBinder> outBinder;
@@ -775,7 +821,11 @@
     return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
 }
 
-TEST_P(BinderRpcThreads, ThreadPoolGreaterThanEqualRequested) {
+TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
     constexpr size_t kNumThreads = 10;
 
     auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
@@ -788,12 +838,12 @@
         ts.push_back(std::thread([&] { proc.rootIface->lockUnlock(); }));
     }
 
-    usleep(100000); // give chance for calls on other threads
+    usleep(10000); // give chance for calls on other threads
 
     // other calls still work
     EXPECT_EQ(OK, proc.rootBinder->pingBinder());
 
-    constexpr size_t blockTimeMs = 500;
+    constexpr size_t blockTimeMs = 50;
     size_t epochMsBefore = epochMillis();
     // after this, we should never see a response within this time
     EXPECT_OK(proc.rootIface->unlockInMsAsync(blockTimeMs));
@@ -826,14 +876,22 @@
     EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs);
 }
 
-TEST_P(BinderRpcThreads, ThreadPoolOverSaturated) {
+TEST_P(BinderRpc, ThreadPoolOverSaturated) {
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
     constexpr size_t kNumThreads = 10;
     constexpr size_t kNumCalls = kNumThreads + 3;
     auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
     testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
 }
 
-TEST_P(BinderRpcThreads, ThreadPoolLimitOutgoing) {
+TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
     constexpr size_t kNumThreads = 20;
     constexpr size_t kNumOutgoingConnections = 10;
     constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
@@ -842,7 +900,11 @@
     testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
 }
 
-TEST_P(BinderRpcThreads, ThreadingStressTest) {
+TEST_P(BinderRpc, ThreadingStressTest) {
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
     constexpr size_t kNumClientThreads = 10;
     constexpr size_t kNumServerThreads = 10;
     constexpr size_t kNumCalls = 100;
@@ -871,7 +933,11 @@
     for (auto& t : threads) t.join();
 }
 
-TEST_P(BinderRpcThreads, OnewayStressTest) {
+TEST_P(BinderRpc, OnewayStressTest) {
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
     constexpr size_t kNumClientThreads = 10;
     constexpr size_t kNumServerThreads = 10;
     constexpr size_t kNumCalls = 1000;
@@ -906,7 +972,50 @@
     EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
 }
 
-TEST_P(BinderRpcThreads, OnewayCallQueueing) {
+TEST_P(BinderRpc, OnewayCallQueueingWithFds) {
+    if (!supportsFdTransport()) {
+        GTEST_SKIP() << "Would fail trivially (which is tested elsewhere)";
+    }
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
+    // This test forces a oneway transaction to be queued by issuing two
+    // `blockingSendFdOneway` calls, then drains the queue by issuing two
+    // `blockingRecvFd` calls.
+    //
+    // For more details about the queuing semantics see
+    // https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY
+
+    auto proc = createRpcTestSocketServerProcess({
+            .numThreads = 3,
+            .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+            .serverSupportedFileDescriptorTransportModes =
+                    {RpcSession::FileDescriptorTransportMode::UNIX},
+    });
+
+    EXPECT_OK(proc.rootIface->blockingSendFdOneway(
+            android::os::ParcelFileDescriptor(mockFileDescriptor("a"))));
+    EXPECT_OK(proc.rootIface->blockingSendFdOneway(
+            android::os::ParcelFileDescriptor(mockFileDescriptor("b"))));
+
+    android::os::ParcelFileDescriptor fdA;
+    EXPECT_OK(proc.rootIface->blockingRecvFd(&fdA));
+    std::string result;
+    CHECK(android::base::ReadFdToString(fdA.get(), &result));
+    EXPECT_EQ(result, "a");
+
+    android::os::ParcelFileDescriptor fdB;
+    EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB));
+    CHECK(android::base::ReadFdToString(fdB.get(), &result));
+    EXPECT_EQ(result, "b");
+}
+
+TEST_P(BinderRpc, OnewayCallQueueing) {
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
     constexpr size_t kNumSleeps = 10;
     constexpr size_t kNumExtraServerThreads = 4;
     constexpr size_t kSleepMs = 50;
@@ -935,7 +1044,11 @@
     saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
 }
 
-TEST_P(BinderRpcThreads, OnewayCallExhaustion) {
+TEST_P(BinderRpc, OnewayCallExhaustion) {
+    if (clientOrServerSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
     constexpr size_t kNumClients = 2;
     constexpr size_t kTooLongMs = 1000;
 
@@ -978,17 +1091,16 @@
 TEST_P(BinderRpc, Callbacks) {
     const static std::string kTestString = "good afternoon!";
 
-    bool bothSingleThreaded = !kEnableRpcThreads || singleThreaded();
-
     for (bool callIsOneway : {true, false}) {
         for (bool callbackIsOneway : {true, false}) {
             for (bool delayed : {true, false}) {
-                if (bothSingleThreaded && (callIsOneway || callbackIsOneway || delayed)) {
+                if (clientOrServerSingleThreaded() &&
+                    (callIsOneway || callbackIsOneway || delayed)) {
                     // we have no incoming connections to receive the callback
                     continue;
                 }
 
-                size_t numIncomingConnections = bothSingleThreaded ? 0 : 1;
+                size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1;
                 auto proc = createRpcTestSocketServerProcess(
                         {.numThreads = 1,
                          .numSessions = 1,
@@ -1036,7 +1148,7 @@
 }
 
 TEST_P(BinderRpc, SingleDeathRecipient) {
-    if (singleThreaded() || !kEnableRpcThreads) {
+    if (clientOrServerSingleThreaded()) {
         GTEST_SKIP() << "This test requires multiple threads";
     }
     class MyDeathRec : public IBinder::DeathRecipient {
@@ -1062,10 +1174,7 @@
     }
 
     std::unique_lock<std::mutex> lock(dr->mMtx);
-    if (!dr->dead) {
-        EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 1000ms));
-    }
-    EXPECT_TRUE(dr->dead) << "Failed to receive the death notification.";
+    ASSERT_TRUE(dr->mCv.wait_for(lock, 100ms, [&]() { return dr->dead; }));
 
     // need to wait for the session to shutdown so we don't "Leak session"
     EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
@@ -1073,7 +1182,7 @@
 }
 
 TEST_P(BinderRpc, SingleDeathRecipientOnShutdown) {
-    if (singleThreaded() || !kEnableRpcThreads) {
+    if (clientOrServerSingleThreaded()) {
         GTEST_SKIP() << "This test requires multiple threads";
     }
     class MyDeathRec : public IBinder::DeathRecipient {
@@ -1100,7 +1209,7 @@
 
     std::unique_lock<std::mutex> lock(dr->mMtx);
     if (!dr->dead) {
-        EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 1000ms));
+        EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 100ms));
     }
     EXPECT_TRUE(dr->dead) << "Failed to receive the death notification.";
 
@@ -1127,7 +1236,7 @@
 }
 
 TEST_P(BinderRpc, UnlinkDeathRecipient) {
-    if (singleThreaded() || !kEnableRpcThreads) {
+    if (clientOrServerSingleThreaded()) {
         GTEST_SKIP() << "This test requires multiple threads";
     }
     class MyDeathRec : public IBinder::DeathRecipient {
@@ -1193,7 +1302,7 @@
     // libbinder.so (when using static libraries, even a client and service
     // using the same kind of static library should have separate copies of the
     // variables).
-    if (!kEnableSharedLibs || singleThreaded() || noKernel()) {
+    if (!kEnableSharedLibs || serverSingleThreaded() || noKernel()) {
         GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
                         "at build time.";
     }
@@ -1393,7 +1502,11 @@
     return ret;
 }
 
-TEST_P(BinderRpcThreads, Fds) {
+TEST_P(BinderRpc, Fds) {
+    if (serverSingleThreaded()) {
+        GTEST_SKIP() << "This test requires multiple threads";
+    }
+
     ssize_t beforeFds = countFds();
     ASSERT_GE(beforeFds, 0);
     {
@@ -1503,7 +1616,7 @@
 }
 
 static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
-    std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET};
+    std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET};
 
     if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED);
 
@@ -1534,15 +1647,6 @@
                                            ::testing::Values(false, true)),
                         BinderRpc::PrintParamInfo);
 
-INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpcThreads,
-                        ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
-                                           ::testing::ValuesIn(RpcSecurityValues()),
-                                           ::testing::ValuesIn(testVersions()),
-                                           ::testing::ValuesIn(testVersions()),
-                                           ::testing::Values(false),
-                                           ::testing::Values(false, true)),
-                        BinderRpc::PrintParamInfo);
-
 class BinderRpcServerRootObject
       : public ::testing::TestWithParam<std::tuple<bool, bool, RpcSecurity>> {};
 
@@ -1594,36 +1698,6 @@
     bool mValue = false;
 };
 
-TEST_P(BinderRpcServerOnly, Shutdown) {
-    if constexpr (!kEnableRpcThreads) {
-        GTEST_SKIP() << "Test skipped because threads were disabled at build time";
-    }
-
-    auto addr = allocateSocketAddress();
-    auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
-    server->setProtocolVersion(std::get<1>(GetParam()));
-    ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
-    auto joinEnds = std::make_shared<OneOffSignal>();
-
-    // If things are broken and the thread never stops, don't block other tests. Because the thread
-    // may run after the test finishes, it must not access the stack memory of the test. Hence,
-    // shared pointers are passed.
-    std::thread([server, joinEnds] {
-        server->join();
-        joinEnds->notify();
-    }).detach();
-
-    bool shutdown = false;
-    for (int i = 0; i < 10 && !shutdown; i++) {
-        usleep(300 * 1000); // 300ms; total 3s
-        if (server->shutdown()) shutdown = true;
-    }
-    ASSERT_TRUE(shutdown) << "server->shutdown() never returns true";
-
-    ASSERT_TRUE(joinEnds->wait(2s))
-            << "After server->shutdown() returns true, join() did not stop after 2s";
-}
-
 TEST(BinderRpc, Java) {
 #if !defined(__ANDROID__)
     GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on"
@@ -1676,6 +1750,57 @@
     ASSERT_EQ(OK, rpcBinder->pingBinder());
 }
 
+class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> {
+public:
+    static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) {
+        return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" +
+                std::to_string(std::get<1>(info.param));
+    }
+};
+
+TEST_P(BinderRpcServerOnly, SetExternalServerTest) {
+    base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+    int sinkFd = sink.get();
+    auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
+    server->setProtocolVersion(std::get<1>(GetParam()));
+    ASSERT_FALSE(server->hasServer());
+    ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
+    ASSERT_TRUE(server->hasServer());
+    base::unique_fd retrieved = server->releaseServer();
+    ASSERT_FALSE(server->hasServer());
+    ASSERT_EQ(sinkFd, retrieved.get());
+}
+
+TEST_P(BinderRpcServerOnly, Shutdown) {
+    if constexpr (!kEnableRpcThreads) {
+        GTEST_SKIP() << "Test skipped because threads were disabled at build time";
+    }
+
+    auto addr = allocateSocketAddress();
+    auto server = RpcServer::make(newFactory(std::get<0>(GetParam())));
+    server->setProtocolVersion(std::get<1>(GetParam()));
+    ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
+    auto joinEnds = std::make_shared<OneOffSignal>();
+
+    // If things are broken and the thread never stops, don't block other tests. Because the thread
+    // may run after the test finishes, it must not access the stack memory of the test. Hence,
+    // shared pointers are passed.
+    std::thread([server, joinEnds] {
+        server->join();
+        joinEnds->notify();
+    }).detach();
+
+    bool shutdown = false;
+    for (int i = 0; i < 10 && !shutdown; i++) {
+        usleep(30 * 1000); // 30ms; total 300ms
+        if (server->shutdown()) shutdown = true;
+    }
+    ASSERT_TRUE(shutdown) << "server->shutdown() never returns true";
+
+    ASSERT_TRUE(joinEnds->wait(2s))
+            << "After server->shutdown() returns true, join() did not stop after 2s";
+}
+
 INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerOnly,
                         ::testing::Combine(::testing::ValuesIn(RpcSecurityValues()),
                                            ::testing::ValuesIn(testVersions())),
@@ -1692,6 +1817,8 @@
     // A server that handles client socket connections.
     class Server {
     public:
+        using AcceptConnection = std::function<base::unique_fd(Server*)>;
+
         explicit Server() {}
         Server(Server&&) = default;
         ~Server() { shutdownAndWait(); }
@@ -1716,6 +1843,21 @@
                         return connectTo(UnixSocketAddress(addr.c_str()));
                     };
                 } break;
+                case SocketType::UNIX_BOOTSTRAP: {
+                    base::unique_fd bootstrapFdClient, bootstrapFdServer;
+                    if (!base::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) {
+                        return AssertionFailure() << "Socketpair() failed";
+                    }
+                    auto status = rpcServer->setupUnixDomainSocketBootstrapServer(
+                            std::move(bootstrapFdServer));
+                    if (status != OK) {
+                        return AssertionFailure() << "setupUnixDomainSocketBootstrapServer: "
+                                                  << statusToString(status);
+                    }
+                    mBootstrapSocket = RpcTransportFd(std::move(bootstrapFdClient));
+                    mAcceptConnection = &Server::recvmsgServerConnection;
+                    mConnectToServer = [this] { return connectToUnixBootstrap(mBootstrapSocket); };
+                } break;
                 case SocketType::VSOCK: {
                     auto port = allocateVsockPort();
                     auto status = rpcServer->setupVsockServer(port);
@@ -1748,7 +1890,7 @@
                 }
             }
             mFd = rpcServer->releaseServer();
-            if (!mFd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
+            if (!mFd.fd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
             mCtx = newFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx();
             if (mCtx == nullptr) return AssertionFailure() << "newServerCtx";
             mSetup = true;
@@ -1763,14 +1905,33 @@
             LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
             mThread = std::make_unique<std::thread>(&Server::run, this);
         }
+
+        base::unique_fd acceptServerConnection() {
+            return base::unique_fd(TEMP_FAILURE_RETRY(
+                    accept4(mFd.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK)));
+        }
+
+        base::unique_fd recvmsgServerConnection() {
+            std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+            int buf;
+            iovec iov{&buf, sizeof(buf)};
+
+            if (receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
+                int savedErrno = errno;
+                LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno);
+            }
+            if (fds.size() != 1) {
+                LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size();
+            }
+            return std::move(std::get<base::unique_fd>(fds[0]));
+        }
+
         void run() {
             LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
 
             std::vector<std::thread> threads;
             while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) {
-                base::unique_fd acceptedFd(
-                        TEMP_FAILURE_RETRY(accept4(mFd.get(), nullptr, nullptr /*length*/,
-                                                   SOCK_CLOEXEC | SOCK_NONBLOCK)));
+                base::unique_fd acceptedFd = mAcceptConnection(this);
                 threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd));
             }
 
@@ -1778,7 +1939,8 @@
         }
         void handleOne(android::base::unique_fd acceptedFd) {
             ASSERT_TRUE(acceptedFd.ok());
-            auto serverTransport = mCtx->newTransport(std::move(acceptedFd), mFdTrigger.get());
+            RpcTransportFd transportFd(std::move(acceptedFd));
+            auto serverTransport = mCtx->newTransport(std::move(transportFd), mFdTrigger.get());
             if (serverTransport == nullptr) return; // handshake failed
             ASSERT_TRUE(mPostConnect(serverTransport.get(), mFdTrigger.get()));
         }
@@ -1796,8 +1958,9 @@
     private:
         std::unique_ptr<std::thread> mThread;
         ConnectToServer mConnectToServer;
+        AcceptConnection mAcceptConnection = &Server::acceptServerConnection;
         std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
-        base::unique_fd mFd;
+        RpcTransportFd mFd, mBootstrapSocket;
         std::unique_ptr<RpcTransportCtx> mCtx;
         std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
                 std::make_shared<RpcCertificateVerifierSimple>();
@@ -1844,7 +2007,7 @@
         // connect() and do handshake
         bool setUpTransport() {
             mFd = mConnectToServer();
-            if (!mFd.ok()) return AssertionFailure() << "Cannot connect to server";
+            if (!mFd.fd.ok()) return AssertionFailure() << "Cannot connect to server";
             mClientTransport = mCtx->newTransport(std::move(mFd), mFdTrigger.get());
             return mClientTransport != nullptr;
         }
@@ -1873,9 +2036,11 @@
             ASSERT_EQ(readOk, readMessage());
         }
 
+        bool isTransportWaiting() { return mClientTransport->isWaiting(); }
+
     private:
         ConnectToServer mConnectToServer;
-        base::unique_fd mFd;
+        RpcTransportFd mFd;
         std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
         std::unique_ptr<RpcTransportCtx> mCtx;
         std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
@@ -2122,6 +2287,56 @@
     ASSERT_FALSE(client.readMessage(msg2));
 }
 
+TEST_P(RpcTransportTest, CheckWaitingForRead) {
+    std::mutex readMutex;
+    std::condition_variable readCv;
+    bool shouldContinueReading = false;
+    // Server will write data on transport once its started
+    auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
+        std::string message(RpcTransportTestUtils::kMessage);
+        iovec messageIov{message.data(), message.size()};
+        auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1,
+                                                               std::nullopt, nullptr);
+        if (status != OK) return AssertionFailure() << statusToString(status);
+
+        {
+            std::unique_lock<std::mutex> lock(readMutex);
+            shouldContinueReading = true;
+            lock.unlock();
+            readCv.notify_all();
+        }
+        return AssertionSuccess();
+    };
+
+    // Setup Server and client
+    auto server = std::make_unique<Server>();
+    ASSERT_TRUE(server->setUp(GetParam()));
+
+    Client client(server->getConnectToServerFn());
+    ASSERT_TRUE(client.setUp(GetParam()));
+
+    ASSERT_EQ(OK, trust(&client, server));
+    ASSERT_EQ(OK, trust(server, &client));
+    server->setPostConnect(serverPostConnect);
+
+    server->start();
+    ASSERT_TRUE(client.setUpTransport());
+    {
+        // Wait till server writes data
+        std::unique_lock<std::mutex> lock(readMutex);
+        ASSERT_TRUE(readCv.wait_for(lock, 3s, [&] { return shouldContinueReading; }));
+    }
+
+    // Since there is no read polling here, we will get polling count 0
+    ASSERT_FALSE(client.isTransportWaiting());
+    ASSERT_TRUE(client.readMessage(RpcTransportTestUtils::kMessage));
+    // Thread should increment polling count, read and decrement polling count
+    // Again, polling count should be zero here
+    ASSERT_FALSE(client.isTransportWaiting());
+
+    server->shutdown();
+}
+
 INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest,
                         ::testing::ValuesIn(RpcTransportTest::getRpcTranportTestParams()),
                         RpcTransportTest::PrintParamInfo);
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 4513d36..823bbf6 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -49,6 +49,7 @@
 
 #include "../BuildFlags.h"
 #include "../FdTrigger.h"
+#include "../OS.h"               // for testing UnixBootstrap clients
 #include "../RpcSocketAddress.h" // for testing preconnected clients
 #include "../RpcState.h"         // for debugging
 #include "../vm_sockets.h"       // for VMADDR_*
@@ -67,15 +68,19 @@
 enum class SocketType {
     PRECONNECTED,
     UNIX,
+    UNIX_BOOTSTRAP,
     VSOCK,
     INET,
 };
+
 static inline std::string PrintToString(SocketType socketType) {
     switch (socketType) {
         case SocketType::PRECONNECTED:
             return "preconnected_uds";
         case SocketType::UNIX:
             return "unix_domain_socket";
+        case SocketType::UNIX_BOOTSTRAP:
+            return "unix_domain_socket_bootstrap";
         case SocketType::VSOCK:
             return "vm_socket";
         case SocketType::INET:
@@ -167,6 +172,42 @@
     return readFd;
 }
 
+// A threadsafe channel where writes block until the value is read.
+template <typename T>
+class HandoffChannel {
+public:
+    void write(T v) {
+        {
+            RpcMutexUniqueLock lock(mMutex);
+            // Wait for space to send.
+            mCvEmpty.wait(lock, [&]() { return !mValue.has_value(); });
+            mValue.emplace(std::move(v));
+        }
+        mCvFull.notify_all();
+        RpcMutexUniqueLock lock(mMutex);
+        // Wait for it to be taken.
+        mCvEmpty.wait(lock, [&]() { return !mValue.has_value(); });
+    }
+
+    T read() {
+        RpcMutexUniqueLock lock(mMutex);
+        if (!mValue.has_value()) {
+            mCvFull.wait(lock, [&]() { return mValue.has_value(); });
+        }
+        T v = std::move(mValue.value());
+        mValue.reset();
+        lock.unlock();
+        mCvEmpty.notify_all();
+        return std::move(v);
+    }
+
+private:
+    RpcMutex mMutex;
+    RpcConditionVariable mCvEmpty;
+    RpcConditionVariable mCvFull;
+    std::optional<T> mValue;
+};
+
 using android::binder::Status;
 
 class MyBinderRpcSession : public BnBinderRpcSession {
@@ -374,6 +415,18 @@
         out->reset(mockFileDescriptor(acc));
         return Status::ok();
     }
+
+    HandoffChannel<android::base::unique_fd> mFdChannel;
+
+    Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& fd) override {
+        mFdChannel.write(android::base::unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0)));
+        return Status::ok();
+    }
+
+    Status blockingRecvFd(android::os::ParcelFileDescriptor* fd) override {
+        fd->reset(mFdChannel.read());
+        return Status::ok();
+    }
 };
 
 } // namespace android
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 31eb5da..a922b21 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -42,6 +42,7 @@
     server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
 
     unsigned int outPort = 0;
+    base::unique_fd unixBootstrapFd(serverConfig.unixBootstrapFd);
 
     switch (socketType) {
         case SocketType::PRECONNECTED:
@@ -50,6 +51,9 @@
             CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str()))
                     << serverConfig.addr;
             break;
+        case SocketType::UNIX_BOOTSTRAP:
+            CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(unixBootstrapFd)));
+            break;
         case SocketType::VSOCK:
             CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
             break;
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 0210237..3904e1d 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -84,6 +84,7 @@
         },
     },
     srcs: [
+        "random_binder.cpp",
         "random_fd.cpp",
         "random_parcel.cpp",
         "libbinder_driver.cpp",
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
index 81e79b5..d19f25b 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -18,29 +18,27 @@
 #include <android/binder_auto_utils.h>
 #include <vector>
 
+#include <android/binder_libbinder.h>
 #include <android/binder_parcel.h>
 #include "parcel_fuzzer.h"
 
-// libbinder_ndk doesn't export this header which breaks down its API for NDK
-// and APEX users, but we need access to it to fuzz.
-#include "../../ndk/parcel_internal.h"
-
 class NdkParcelAdapter {
 public:
-    NdkParcelAdapter() : mParcel(new AParcel(nullptr /*binder*/)) {}
+    NdkParcelAdapter() : mParcel(AParcel_create()) {}
 
     const AParcel* aParcel() const { return mParcel.get(); }
     AParcel* aParcel() { return mParcel.get(); }
 
-    android::Parcel* parcel() { return aParcel()->get(); }
+    const android::Parcel* parcel() const { return AParcel_viewPlatformParcel(aParcel()); }
+    android::Parcel* parcel() { return AParcel_viewPlatformParcel(aParcel()); }
 
-    const uint8_t* data() const { return aParcel()->get()->data(); }
-    size_t dataSize() const { return aParcel()->get()->dataSize(); }
-    size_t dataAvail() const { return aParcel()->get()->dataAvail(); }
-    size_t dataPosition() const { return aParcel()->get()->dataPosition(); }
-    size_t dataCapacity() const { return aParcel()->get()->dataCapacity(); }
+    const uint8_t* data() const { return parcel()->data(); }
+    size_t dataSize() const { return parcel()->dataSize(); }
+    size_t dataAvail() const { return parcel()->dataAvail(); }
+    size_t dataPosition() const { return parcel()->dataPosition(); }
+    size_t dataCapacity() const { return parcel()->dataCapacity(); }
     android::status_t setData(const uint8_t* buffer, size_t len) {
-        return aParcel()->get()->setData(buffer, len);
+        return parcel()->setData(buffer, len);
     }
 
     android::status_t appendFrom(const NdkParcelAdapter* parcel, int32_t start, int32_t len) {
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h
similarity index 63%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h
index 9fac3e8..8fc9263 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,16 @@
  * limitations under the License.
  */
 
-package android.gui;
+#pragma once
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
-}
+#include <binder/IBinder.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+// Get a random binder object for use in fuzzing.
+//
+// May return nullptr.
+sp<IBinder> getRandomBinder(FuzzedDataProvider* provider);
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 32494e3..25f6096 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -17,6 +17,9 @@
 
 #include <fuzzbinder/random_parcel.h>
 
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+
 namespace android {
 
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) {
@@ -60,6 +63,15 @@
             options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
         }
     }
+
+    // invariants
+
+    auto ps = ProcessState::selfOrNull();
+    if (ps) {
+        CHECK_EQ(0, ps->getThreadPoolMaxTotalThreadCount())
+                << "Binder threadpool should not be started by fuzzer because coverage can only "
+                   "cover in-process calls.";
+    }
 }
 
 } // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/random_binder.cpp b/libs/binder/tests/parcel_fuzzer/random_binder.cpp
new file mode 100644
index 0000000..8a1fecb
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/random_binder.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <fuzzbinder/random_binder.h>
+
+#include <fuzzbinder/random_parcel.h>
+
+#include <android-base/logging.h>
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+class RandomBinder : public BBinder {
+public:
+    RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes)
+          : mDescriptor(descriptor),
+            mBytes(std::move(bytes)),
+            mProvider(mBytes.data(), mBytes.size()) {}
+    const String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override {
+        (void)code;
+        (void)data;
+        (void)reply;
+        (void)flags; // note - for maximum coverage even ignore if oneway
+
+        if (mProvider.ConsumeBool()) {
+            return mProvider.ConsumeIntegral<status_t>();
+        }
+
+        if (reply == nullptr) return OK;
+
+        // TODO: things we could do to increase state space
+        // - also pull FDs and binders from 'data'
+        //     (optionally combine these into random parcel 'options')
+        // - also pull FDs and binders from random parcel 'options'
+        RandomParcelOptions options;
+
+        // random output
+        std::vector<uint8_t> subData = mProvider.ConsumeBytes<uint8_t>(
+                mProvider.ConsumeIntegralInRange<size_t>(0, mProvider.remaining_bytes()));
+        fillRandomParcel(reply, FuzzedDataProvider(subData.data(), subData.size()), &options);
+
+        return OK;
+    }
+
+private:
+    String16 mDescriptor;
+
+    // note may not all be used
+    std::vector<uint8_t> mBytes;
+    FuzzedDataProvider mProvider;
+};
+
+sp<IBinder> getRandomBinder(FuzzedDataProvider* provider) {
+    auto makeFunc = provider->PickValueInArray<const std::function<sp<IBinder>()>>({
+            [&]() {
+                // descriptor is the length of a class name, e.g.
+                // "some.package.Foo"
+                std::string str = provider->ConsumeRandomLengthString(100 /*max length*/);
+
+                // arbitrarily consume remaining data to create a binder that can return
+                // random results - coverage guided fuzzer should ensure all of the remaining
+                // data isn't always used
+                std::vector<uint8_t> bytes = provider->ConsumeBytes<uint8_t>(
+                        provider->ConsumeIntegralInRange<size_t>(0, provider->remaining_bytes()));
+
+                return new RandomBinder(String16(str.c_str()), std::move(bytes));
+            },
+            []() {
+                // this is the easiest remote binder to get ahold of, and it
+                // should be able to handle anything thrown at it, and
+                // essentially every process can talk to it, so it's a good
+                // candidate for checking usage of an actual BpBinder
+                return IInterface::asBinder(defaultServiceManager());
+            },
+            [&]() -> sp<IBinder> { return nullptr; },
+    });
+    return makeFunc();
+}
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
index 3fcf104..e4dbb2d 100644
--- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
@@ -26,9 +26,12 @@
 using base::unique_fd;
 
 std::vector<unique_fd> getRandomFds(FuzzedDataProvider* provider) {
+    const char* fdType;
+
     std::vector<unique_fd> fds = provider->PickValueInArray<
             std::function<std::vector<unique_fd>()>>({
             [&]() {
+                fdType = "ashmem";
                 std::vector<unique_fd> ret;
                 ret.push_back(unique_fd(
                         ashmem_create_region("binder test region",
@@ -36,18 +39,21 @@
                 return ret;
             },
             [&]() {
+                fdType = "/dev/null";
                 std::vector<unique_fd> ret;
                 ret.push_back(unique_fd(open("/dev/null", O_RDWR)));
                 return ret;
             },
             [&]() {
+                fdType = "pipefd";
+
                 int pipefds[2];
 
                 int flags = O_CLOEXEC;
                 if (provider->ConsumeBool()) flags |= O_DIRECT;
                 if (provider->ConsumeBool()) flags |= O_NONBLOCK;
 
-                CHECK_EQ(0, pipe2(pipefds, flags));
+                CHECK_EQ(0, pipe2(pipefds, flags)) << flags;
 
                 if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]);
 
@@ -58,7 +64,7 @@
             },
     })();
 
-    for (const auto& fd : fds) CHECK(fd.ok()) << fd.get();
+    for (const auto& fd : fds) CHECK(fd.ok()) << fd.get() << " " << fdType;
 
     return fds;
 }
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 51cb768..edc695f 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -17,23 +17,14 @@
 #include <fuzzbinder/random_parcel.h>
 
 #include <android-base/logging.h>
-#include <binder/IServiceManager.h>
 #include <binder/RpcSession.h>
 #include <binder/RpcTransportRaw.h>
+#include <fuzzbinder/random_binder.h>
 #include <fuzzbinder/random_fd.h>
 #include <utils/String16.h>
 
 namespace android {
 
-class NamedBinder : public BBinder {
-public:
-    NamedBinder(const String16& descriptor) : mDescriptor(descriptor) {}
-    const String16& getInterfaceDescriptor() const override { return mDescriptor; }
-
-private:
-    String16 mDescriptor;
-};
-
 static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) {
     std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes());
     CHECK(OK == p->write(data.data(), data.size()));
@@ -45,6 +36,11 @@
     if (provider.ConsumeBool()) {
         auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
         CHECK_EQ(OK, session->addNullDebuggingClient());
+        // Set the protocol version so that we don't crash if the session
+        // actually gets used. This isn't cheating because the version should
+        // always be set if the session init succeeded and we aren't testing the
+        // session init here (it is bypassed by addNullDebuggingClient).
+        session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION);
         p->markForRpc(session);
 
         if (options->writeHeader) {
@@ -89,32 +85,16 @@
                 },
                 // write binder
                 [&]() {
-                    auto makeFunc = provider.PickValueInArray<const std::function<sp<IBinder>()>>({
-                            [&]() {
-                                // descriptor is the length of a class name, e.g.
-                                // "some.package.Foo"
-                                std::string str =
-                                        provider.ConsumeRandomLengthString(100 /*max length*/);
-                                return new NamedBinder(String16(str.c_str()));
-                            },
-                            []() {
-                                // this is the easiest remote binder to get ahold of, and it
-                                // should be able to handle anything thrown at it, and
-                                // essentially every process can talk to it, so it's a good
-                                // candidate for checking usage of an actual BpBinder
-                                return IInterface::asBinder(defaultServiceManager());
-                            },
-                            [&]() -> sp<IBinder> {
-                                if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
-                                    return options->extraBinders.at(
-                                            provider.ConsumeIntegralInRange<
-                                                    size_t>(0, options->extraBinders.size() - 1));
-                                } else {
-                                    return nullptr;
-                                }
-                            },
-                    });
-                    sp<IBinder> binder = makeFunc();
+                    sp<IBinder> binder;
+                    if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
+                        binder = options->extraBinders.at(
+                                provider.ConsumeIntegralInRange<size_t>(0,
+                                                                        options->extraBinders
+                                                                                        .size() -
+                                                                                1));
+                    } else {
+                        binder = getRandomBinder(&provider);
+                    }
                     CHECK(OK == p->writeStrongBinder(binder));
                 },
         });
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index a8713a2..f68a561 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -157,8 +157,6 @@
         }
     }
 
-    usleep(10000);
-
     if (hangupBeforeShutdown) {
         connections.clear();
         while (!server->listSessions().empty() || server->numUninitializedSessions()) {
@@ -167,6 +165,10 @@
         }
     }
 
+    while (server->hasActiveRequests()) {
+        usleep(10);
+    }
+
     while (!server->shutdown()) usleep(1);
     serverThread.join();
 
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 187add4..397ff41 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -14,22 +14,63 @@
  * limitations under the License.
  */
 
+#if defined(TRUSTY_USERSPACE)
 #include <openssl/rand.h>
+#include <trusty_ipc.h>
+#else
+#include <lib/rand/rand.h>
+#endif
+
+#include <binder/RpcTransportTipcTrusty.h>
 
 #include "../OS.h"
+#include "TrustyStatus.h"
 
 using android::base::Result;
 
 namespace android {
 
-Result<void> setNonBlocking(android::base::borrowed_fd fd) {
+Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) {
     // Trusty IPC syscalls are all non-blocking by default.
     return {};
 }
 
 status_t getRandomBytes(uint8_t* data, size_t size) {
+#if defined(TRUSTY_USERSPACE)
     int res = RAND_bytes(data, size);
     return res == 1 ? OK : UNKNOWN_ERROR;
+#else
+    int res = rand_get_bytes(data, size);
+    return res == 0 ? OK : UNKNOWN_ERROR;
+#endif // TRUSTY_USERSPACE
+}
+
+status_t dupFileDescriptor(int oldFd, int* newFd) {
+    int res = dup(oldFd);
+    if (res < 0) {
+        return statusFromTrusty(res);
+    }
+
+    *newFd = res;
+    return OK;
+}
+
+std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() {
+    return RpcTransportCtxFactoryTipcTrusty::make();
+}
+
+int sendMessageOnSocket(
+        const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
+        const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) {
+    errno = ENOTSUP;
+    return -1;
+}
+
+int receiveMessageFromSocket(
+        const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
+        std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) {
+    errno = ENOTSUP;
+    return -1;
 }
 
 } // namespace android
diff --git a/libs/binder/trusty/README.md b/libs/binder/trusty/README.md
index 1a273aa..8a60af8 100644
--- a/libs/binder/trusty/README.md
+++ b/libs/binder/trusty/README.md
@@ -1,39 +1,45 @@
 # Binder for Trusty
 
 This is the Trusty port of the libbinder library.
-To build it, take the following steps:
-
-* Check out copies of the Trusty and AOSP repositories.
-* Apply the patches from the `trusty_binder` topic on both repositories.
-* Build Trusty normally using `build.py`.
-* Run the sample AIDL test for Trusty:
-  ```shell
-  $ ./build-root/.../run --headless --boot-test com.android.trusty.aidl.test
-  ```
-
-To run the Android-Trusty IPC test, do the following:
-
-* Build AOSP for the `qemu_trusty_arm64-userdebug` target:
-  ```shell
-  $ lunch qemu_trusty_arm64-userdebug
-  $ m
-  ```
-* In the Trusty directory, run the emulator with the newly built Android:
-  ```shell
-  $ ./build-root/.../run --android /path/to/aosp
-  ```
-* Using either `adb` or the shell inside the emulator itself, run the Trusty
-  Binder test as root:
-  ```shell
-  # /data/nativetest64/vendor/trusty_binder_test/trusty_binder_test
-  ```
-
-## Running the AIDL compiler
-For now, you will need to run the AIDL compiler manually to generate the C++
-source code for Trusty clients and services. The general syntax is:
+To build it, first you will need a checkout of the Trusty tree:
 ```shell
-$ aidl --lang=cpp -o <output directory> -h <output header directory> <AIDL files...>
+$ mkdir /path/to/trusty
+$ cd /path/to/trusty
+$ repo init -u https://android.googlesource.com/trusty/manifest -b master
+$ repo sync -j$(nproc) -c --no-tags
 ```
 
-The compiler will emit some `.cpp` files in the output directory and their
-corresponding `.h` files in the header directory.
+After the checkout is complete, you can use the `build.py` script for both
+building and testing Trusty. For a quick build without any tests, run:
+```shell
+$ ./trusty/vendor/google/aosp/scripts/build.py generic-arm64-test-debug
+```
+This will build the smaller `generic-arm64-test-debug` project which
+does not run any tests.
+
+The qemu-generic-arm64-test-debug` project includes the QEMU emulator and
+a full Trusty test suite, including a set of libbinder tests.
+To run the latter, use the command:
+```shell
+$ ./trusty/vendor/google/aosp/scripts/build.py \
+    --test "boot-test:com.android.trusty.binder.test" \
+    qemu-generic-arm64-test-debug
+```
+
+## Building AIDL files on Trusty
+To compile AIDL interfaces into Trusty libraries, include the `make/aidl.mk`
+in your `rules.mk` file, e.g.:
+```
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_AIDLS := \
+        $(LOCAL_DIR)/IFoo.aidl \
+
+include make/aidl.mk
+```
+
+## Examples
+The Trusty tree contains some sample test apps at
+`trusty/user/app/sample/binder-test`.
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index e8b91e7..18ce316 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -104,24 +104,40 @@
             return;
         }
 
-        /* Save the session for easy access */
-        *ctx_p = session.get();
+        /* Save the session and connection for the other callbacks */
+        auto* channelContext = new (std::nothrow) ChannelContext;
+        if (channelContext == nullptr) {
+            rc = ERR_NO_MEMORY;
+            return;
+        }
+
+        channelContext->session = std::move(session);
+        channelContext->connection = std::move(result.connection);
+
+        *ctx_p = channelContext;
     };
 
     base::unique_fd clientFd(chan);
+    android::RpcTransportFd transportFd(std::move(clientFd));
+
     std::array<uint8_t, RpcServer::kRpcAddressSize> addr;
     constexpr size_t addrLen = sizeof(*peer);
     memcpy(addr.data(), peer, addrLen);
-    RpcServer::establishConnection(sp(server->mRpcServer), std::move(clientFd), addr, addrLen,
+    RpcServer::establishConnection(sp(server->mRpcServer), std::move(transportFd), addr, addrLen,
                                    joinFn);
 
     return rc;
 }
 
-int RpcServerTrusty::handleMessage(const tipc_port* port, handle_t chan, void* ctx) {
-    auto* session = reinterpret_cast<RpcSession*>(ctx);
-    status_t status = session->state()->drainCommands(session->mConnections.mIncoming[0], session,
-                                                      RpcState::CommandType::ANY);
+int RpcServerTrusty::handleMessage(const tipc_port* /*port*/, handle_t /*chan*/, void* ctx) {
+    auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
+    LOG_ALWAYS_FATAL_IF(channelContext == nullptr,
+                        "bad state: message received on uninitialized channel");
+
+    auto& session = channelContext->session;
+    auto& connection = channelContext->connection;
+    status_t status =
+            session->state()->drainCommands(connection, session, RpcState::CommandType::ANY);
     if (status != OK) {
         LOG_RPC_DETAIL("Binder connection thread closing w/ status %s",
                        statusToString(status).c_str());
@@ -130,13 +146,21 @@
     return NO_ERROR;
 }
 
-void RpcServerTrusty::handleDisconnect(const tipc_port* port, handle_t chan, void* ctx) {}
+void RpcServerTrusty::handleDisconnect(const tipc_port* /*port*/, handle_t /*chan*/,
+                                       void* /*ctx*/) {}
 
 void RpcServerTrusty::handleChannelCleanup(void* ctx) {
-    auto* session = reinterpret_cast<RpcSession*>(ctx);
-    auto& connection = session->mConnections.mIncoming.at(0);
+    auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
+    if (channelContext == nullptr) {
+        return;
+    }
+
+    auto& session = channelContext->session;
+    auto& connection = channelContext->connection;
     LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection),
                         "bad state: connection object guaranteed to be in list");
+
+    delete channelContext;
 }
 
 } // namespace android
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index dc27eb9..58bfe71 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "RpcTransportTipcTrusty"
 
+#include <inttypes.h>
 #include <trusty_ipc.h>
 
 #include <binder/RpcSession.h>
@@ -33,7 +34,7 @@
 // RpcTransport for Trusty.
 class RpcTransportTipcTrusty : public RpcTransport {
 public:
-    explicit RpcTransportTipcTrusty(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
+    explicit RpcTransportTipcTrusty(android::RpcTransportFd socket) : mSocket(std::move(socket)) {}
     ~RpcTransportTipcTrusty() { releaseMessage(); }
 
     status_t pollRead() override {
@@ -45,8 +46,8 @@
     }
 
     status_t interruptableWriteFully(
-            FdTrigger* fdTrigger, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
+            const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/,
             const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
             override {
         if (niovs < 0) {
@@ -58,13 +59,33 @@
             size += iovs[i].iov_len;
         }
 
+        handle_t msgHandles[IPC_MAX_MSG_HANDLES];
         ipc_msg_t msg{
                 .num_iov = static_cast<uint32_t>(niovs),
                 .iov = iovs,
-                .num_handles = 0, // TODO: add ancillaryFds
+                .num_handles = 0,
                 .handles = nullptr,
         };
-        ssize_t rc = send_msg(mSocket.get(), &msg);
+
+        if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
+            if (ancillaryFds->size() > IPC_MAX_MSG_HANDLES) {
+                // This shouldn't happen because we check the FD count in RpcState.
+                ALOGE("Saw too many file descriptors in RpcTransportCtxTipcTrusty: "
+                      "%zu (max is %u). Aborting session.",
+                      ancillaryFds->size(), IPC_MAX_MSG_HANDLES);
+                return BAD_VALUE;
+            }
+
+            for (size_t i = 0; i < ancillaryFds->size(); i++) {
+                msgHandles[i] =
+                        std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i));
+            }
+
+            msg.num_handles = ancillaryFds->size();
+            msg.handles = msgHandles;
+        }
+
+        ssize_t rc = send_msg(mSocket.fd.get(), &msg);
         if (rc == ERR_NOT_ENOUGH_BUFFER) {
             // Peer is blocked, wait until it unblocks.
             // TODO: when tipc supports a send-unblocked handler,
@@ -72,7 +93,7 @@
             // when the handler gets called by the library
             uevent uevt;
             do {
-                rc = ::wait(mSocket.get(), &uevt, INFINITE_TIME);
+                rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME);
                 if (rc < 0) {
                     return statusFromTrusty(rc);
                 }
@@ -83,7 +104,7 @@
 
             // Retry the send, it should go through this time because
             // sending is now unblocked
-            rc = send_msg(mSocket.get(), &msg);
+            rc = send_msg(mSocket.fd.get(), &msg);
         }
         if (rc < 0) {
             return statusFromTrusty(rc);
@@ -95,8 +116,8 @@
     }
 
     status_t interruptableReadFully(
-            FdTrigger* fdTrigger, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
+            const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
         if (niovs < 0) {
             return BAD_VALUE;
@@ -123,13 +144,18 @@
                 return status;
             }
 
+            LOG_ALWAYS_FATAL_IF(mMessageInfo.num_handles > IPC_MAX_MSG_HANDLES,
+                                "Received too many handles %" PRIu32, mMessageInfo.num_handles);
+            bool haveHandles = mMessageInfo.num_handles != 0;
+            handle_t msgHandles[IPC_MAX_MSG_HANDLES];
+
             ipc_msg_t msg{
                     .num_iov = static_cast<uint32_t>(niovs),
                     .iov = iovs,
-                    .num_handles = 0, // TODO: support ancillaryFds
-                    .handles = nullptr,
+                    .num_handles = mMessageInfo.num_handles,
+                    .handles = haveHandles ? msgHandles : 0,
             };
-            ssize_t rc = read_msg(mSocket.get(), mMessageInfo.id, mMessageOffset, &msg);
+            ssize_t rc = read_msg(mSocket.fd.get(), mMessageInfo.id, mMessageOffset, &msg);
             if (rc < 0) {
                 return statusFromTrusty(rc);
             }
@@ -140,6 +166,28 @@
                                 "Message offset exceeds length %zu/%zu", mMessageOffset,
                                 mMessageInfo.len);
 
+            if (haveHandles) {
+                if (ancillaryFds != nullptr) {
+                    ancillaryFds->reserve(ancillaryFds->size() + mMessageInfo.num_handles);
+                    for (size_t i = 0; i < mMessageInfo.num_handles; i++) {
+                        ancillaryFds->emplace_back(base::unique_fd(msgHandles[i]));
+                    }
+
+                    // Clear the saved number of handles so we don't accidentally
+                    // read them multiple times
+                    mMessageInfo.num_handles = 0;
+                    haveHandles = false;
+                } else {
+                    ALOGE("Received unexpected handles %" PRIu32, mMessageInfo.num_handles);
+                    // It should be safe to continue here. We could abort, but then
+                    // peers could DoS us by sending messages with handles in them.
+                    // Close the handles since we are ignoring them.
+                    for (size_t i = 0; i < mMessageInfo.num_handles; i++) {
+                        ::close(msgHandles[i]);
+                    }
+                }
+            }
+
             // Release the message if all of it has been read
             if (mMessageOffset == mMessageInfo.len) {
                 releaseMessage();
@@ -169,6 +217,8 @@
         }
     }
 
+    bool isWaiting() override { return mSocket.isInPollingState(); }
+
 private:
     status_t ensureMessage(bool wait) {
         int rc;
@@ -179,7 +229,7 @@
 
         /* TODO: interruptible wait, maybe with a timeout??? */
         uevent uevt;
-        rc = ::wait(mSocket.get(), &uevt, wait ? INFINITE_TIME : 0);
+        rc = ::wait(mSocket.fd.get(), &uevt, wait ? INFINITE_TIME : 0);
         if (rc < 0) {
             if (rc == ERR_TIMED_OUT && !wait) {
                 // If we timed out with wait==false, then there's no message
@@ -192,7 +242,7 @@
             return OK;
         }
 
-        rc = get_msg(mSocket.get(), &mMessageInfo);
+        rc = get_msg(mSocket.fd.get(), &mMessageInfo);
         if (rc < 0) {
             return statusFromTrusty(rc);
         }
@@ -204,12 +254,12 @@
 
     void releaseMessage() {
         if (mHaveMessage) {
-            put_msg(mSocket.get(), mMessageInfo.id);
+            put_msg(mSocket.fd.get(), mMessageInfo.id);
             mHaveMessage = false;
         }
     }
 
-    base::unique_fd mSocket;
+    android::RpcTransportFd mSocket;
 
     bool mHaveMessage = false;
     ipc_msg_info mMessageInfo;
@@ -219,9 +269,9 @@
 // RpcTransportCtx for Trusty.
 class RpcTransportCtxTipcTrusty : public RpcTransportCtx {
 public:
-    std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd,
+    std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket,
                                                FdTrigger*) const override {
-        return std::make_unique<RpcTransportTipcTrusty>(std::move(fd));
+        return std::make_unique<RpcTransportTipcTrusty>(std::move(socket));
     }
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
 };
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index e8fc9f9..7d9dd8c 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -60,6 +60,10 @@
             std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
     void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); }
+    void setSupportedFileDescriptorTransportModes(
+            const std::vector<RpcSession::FileDescriptorTransportMode>& modes) {
+        mRpcServer->setSupportedFileDescriptorTransportModes(modes);
+    }
     void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); }
     void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); }
     void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) {
@@ -77,6 +81,12 @@
     explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
                              std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize);
 
+    // The Rpc-specific context maintained for every open TIPC channel.
+    struct ChannelContext {
+        sp<RpcSession> session;
+        sp<RpcSession::RpcConnection> connection;
+    };
+
     static int handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, void** ctx_p);
     static int handleMessage(const tipc_port* port, handle_t chan, void* ctx);
     static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx);
diff --git a/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h b/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h
new file mode 100644
index 0000000..2747314
--- /dev/null
+++ b/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 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 <stddef.h>
+#include <trusty_ipc.h>
+#include <uapi/trusty_uuid.h>
+
+struct tipc_port_acl {
+    uint32_t flags;
+    uint32_t uuid_num;
+    const struct uuid** uuids;
+    const void* extra_data;
+};
+
+struct tipc_port {
+    const char* name;
+    uint32_t msg_max_size;
+    uint32_t msg_queue_len;
+    const struct tipc_port_acl* acl;
+    const void* priv;
+};
+
+struct tipc_srv_ops {
+    int (*on_connect)(const struct tipc_port* port, handle_t chan, const struct uuid* peer,
+                      void** ctx_p);
+
+    int (*on_message)(const struct tipc_port* port, handle_t chan, void* ctx);
+
+    void (*on_disconnect)(const struct tipc_port* port, handle_t chan, void* ctx);
+
+    void (*on_channel_cleanup)(void* ctx);
+};
+
+static inline int tipc_add_service(struct tipc_hset*, const struct tipc_port*, uint32_t, uint32_t,
+                                   const struct tipc_srv_ops*) {
+    return 0;
+}
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/binder/trusty/include_mock/openssl/rand.h
similarity index 77%
rename from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
rename to libs/binder/trusty/include_mock/openssl/rand.h
index 9fac3e8..07dcc1c 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/binder/trusty/include_mock/openssl/rand.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,11 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-package android.gui;
-
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
+static inline int RAND_bytes(unsigned char*, int) {
+    return 0;
 }
diff --git a/libs/binder/trusty/include_mock/trusty_ipc.h b/libs/binder/trusty/include_mock/trusty_ipc.h
new file mode 100644
index 0000000..a2170ce
--- /dev/null
+++ b/libs/binder/trusty/include_mock/trusty_ipc.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <uapi/trusty_uuid.h>
+
+#define INFINITE_TIME 1
+#define IPC_MAX_MSG_HANDLES 8
+
+#define IPC_HANDLE_POLL_HUP 0x1
+#define IPC_HANDLE_POLL_MSG 0x2
+#define IPC_HANDLE_POLL_SEND_UNBLOCKED 0x4
+
+typedef int handle_t;
+
+typedef struct ipc_msg {
+    uint32_t num_iov;
+    iovec* iov;
+    uint32_t num_handles;
+    handle_t* handles;
+} ipc_msg_t;
+
+typedef struct ipc_msg_info {
+    size_t len;
+    uint32_t id;
+    uint32_t num_handles;
+} ipc_msg_info_t;
+
+typedef struct uevent {
+    uint32_t event;
+} uevent_t;
+
+static inline handle_t port_create(const char*, uint32_t, uint32_t, uint32_t) {
+    return 0;
+}
+static inline handle_t connect(const char*, uint32_t) {
+    return 0;
+}
+static inline handle_t accept(handle_t, uuid_t*) {
+    return 0;
+}
+static inline int set_cookie(handle_t, void*) {
+    return 0;
+}
+static inline handle_t handle_set_create(void) {
+    return 0;
+}
+static inline int handle_set_ctrl(handle_t, uint32_t, struct uevent*) {
+    return 0;
+}
+static inline int wait(handle_t, uevent_t*, uint32_t) {
+    return 0;
+}
+static inline int wait_any(uevent_t*, uint32_t) {
+    return 0;
+}
+static inline int get_msg(handle_t, ipc_msg_info_t*) {
+    return 0;
+}
+static inline ssize_t read_msg(handle_t, uint32_t, uint32_t, ipc_msg_t*) {
+    return 0;
+}
+static inline int put_msg(handle_t, uint32_t) {
+    return 0;
+}
+static inline ssize_t send_msg(handle_t, ipc_msg_t*) {
+    return 0;
+}
diff --git a/libs/binder/trusty/include_mock/trusty_log.h b/libs/binder/trusty/include_mock/trusty_log.h
new file mode 100644
index 0000000..d51e752
--- /dev/null
+++ b/libs/binder/trusty/include_mock/trusty_log.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stdio.h>
+
+// Mock definitions for the Trusty logging macros. These are not
+// meant to be run, just compiled successfully.
+#define TLOGD(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#define TLOGI(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#define TLOGW(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#define TLOGE(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#define TLOGC(fmt, ...) printf(fmt, ##__VA_ARGS__)
diff --git a/libs/binder/trusty/include_mock/uapi/err.h b/libs/binder/trusty/include_mock/uapi/err.h
new file mode 100644
index 0000000..c7e117e
--- /dev/null
+++ b/libs/binder/trusty/include_mock/uapi/err.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+enum {
+    NO_ERROR,
+    ERR_ACCESS_DENIED,
+    ERR_ALREADY_EXISTS,
+    ERR_BAD_HANDLE,
+    ERR_BAD_LEN,
+    ERR_BAD_STATE,
+    ERR_CHANNEL_CLOSED,
+    ERR_CMD_UNKNOWN,
+    ERR_GENERIC,
+    ERR_INVALID_ARGS,
+    ERR_NO_MEMORY,
+    ERR_NO_MSG,
+    ERR_NOT_ALLOWED,
+    ERR_NOT_CONFIGURED,
+    ERR_NOT_ENOUGH_BUFFER,
+    ERR_NOT_FOUND,
+    ERR_NOT_READY,
+    ERR_NOT_SUPPORTED,
+    ERR_NOT_VALID,
+    ERR_TIMED_OUT,
+    ERR_TOO_BIG,
+};
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/binder/trusty/include_mock/uapi/trusty_uuid.h
similarity index 77%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/binder/trusty/include_mock/uapi/trusty_uuid.h
index 9fac3e8..f636826 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/binder/trusty/include_mock/uapi/trusty_uuid.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,11 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-package android.gui;
-
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
-}
+typedef struct uuid {
+    int placeholder;
+} uuid_t;
diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp
index fd54744..b4243af 100644
--- a/libs/binder/trusty/logging.cpp
+++ b/libs/binder/trusty/logging.cpp
@@ -54,7 +54,7 @@
     abort();
 }
 
-static void TrustyLogLine(const char* msg, int length, android::base::LogSeverity severity,
+static void TrustyLogLine(const char* msg, int /*length*/, android::base::LogSeverity severity,
                           const char* tag) {
     switch (severity) {
         case VERBOSE:
@@ -157,7 +157,7 @@
     TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message);
 }
 
-bool ShouldLog(LogSeverity severity, const char* tag) {
+bool ShouldLog(LogSeverity /*severity*/, const char* /*tag*/) {
     // This is controlled by Trusty's log level.
     return true;
 }
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/binder/trusty/ndk/include/sys/cdefs.h
similarity index 66%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/binder/trusty/ndk/include/sys/cdefs.h
index 9fac3e8..6a48d2b 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/binder/trusty/ndk/include/sys/cdefs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,11 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-package android.gui;
+#include <lk/compiler.h>
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
-}
+/* Alias the bionic macros to the ones from lk/compiler.h */
+#define __BEGIN_DECLS __BEGIN_CDECLS
+#define __END_DECLS __END_CDECLS
+
+#define __INTRODUCED_IN(x) /* nothing on Trusty */
diff --git a/libs/binder/trusty/ndk/rules.mk b/libs/binder/trusty/ndk/rules.mk
new file mode 100644
index 0000000..03fd006
--- /dev/null
+++ b/libs/binder/trusty/ndk/rules.mk
@@ -0,0 +1,38 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+LIBBINDER_NDK_DIR := frameworks/native/libs/binder/ndk
+
+MODULE_SRCS := \
+	$(LIBBINDER_NDK_DIR)/ibinder.cpp \
+	$(LIBBINDER_NDK_DIR)/libbinder.cpp \
+	$(LIBBINDER_NDK_DIR)/parcel.cpp \
+	$(LIBBINDER_NDK_DIR)/status.cpp \
+
+MODULE_EXPORT_INCLUDES += \
+	$(LOCAL_DIR)/include \
+	$(LIBBINDER_NDK_DIR)/include_cpp \
+	$(LIBBINDER_NDK_DIR)/include_ndk \
+	$(LIBBINDER_NDK_DIR)/include_platform \
+
+MODULE_LIBRARY_DEPS += \
+	trusty/user/base/lib/libstdc++-trusty \
+	frameworks/native/libs/binder/trusty \
+
+include make/library.mk
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 83475f5..4e5cd18 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -36,6 +36,7 @@
 	$(LIBBINDER_DIR)/IInterface.cpp \
 	$(LIBBINDER_DIR)/IResultReceiver.cpp \
 	$(LIBBINDER_DIR)/Parcel.cpp \
+	$(LIBBINDER_DIR)/ParcelFileDescriptor.cpp \
 	$(LIBBINDER_DIR)/RpcServer.cpp \
 	$(LIBBINDER_DIR)/RpcSession.cpp \
 	$(LIBBINDER_DIR)/RpcState.cpp \
@@ -75,7 +76,6 @@
 	$(LIBBINDER_DIR)/ndk/include_cpp \
 
 MODULE_EXPORT_COMPILEFLAGS += \
-	-DBINDER_NO_KERNEL_IPC \
 	-DBINDER_RPC_SINGLE_THREADED \
 	-D__ANDROID_VNDK__ \
 
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
index 44e2fd1..df1f35d 100644
--- a/libs/binderthreadstate/test.cpp
+++ b/libs/binderthreadstate/test.cpp
@@ -68,8 +68,22 @@
 
 static void callAidl(size_t id, int32_t idx) {
     sp<IAidlStuff> stuff;
-    CHECK(OK == android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff));
-    CHECK(stuff->call(idx).isOk());
+    CHECK_EQ(OK, android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff));
+    auto ret = stuff->call(idx);
+    CHECK(ret.isOk()) << ret;
+}
+
+static std::string getStackPointerDebugInfo() {
+    const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer();
+    const void* binderSp = android::IPCThreadState::self()->getServingStackPointer();
+
+    std::stringstream ss;
+    ss << "(hwbinder sp: " << hwbinderSp << " binder sp: " << binderSp << ")";
+    return ss.str();
+}
+
+static inline std::ostream& operator<<(std::ostream& o, const BinderCallType& s) {
+    return o << static_cast<std::underlying_type_t<BinderCallType>>(s);
 }
 
 class HidlServer : public IHidlStuff {
@@ -79,21 +93,25 @@
     size_t otherId;
 
     Return<void> callLocal() {
-        CHECK(BinderCallType::NONE == getCurrentServingCall());
+        CHECK_EQ(BinderCallType::NONE, getCurrentServingCall());
         return android::hardware::Status::ok();
     }
     Return<void> call(int32_t idx) {
+        bool doCallHidl = thisId == kP1Id && idx % 4 < 2;
+
         LOG(INFO) << "HidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx
-                  << " with tid: " << gettid();
-        CHECK(BinderCallType::HWBINDER == getCurrentServingCall());
+                  << " with tid: " << gettid() << " calling " << (doCallHidl ? "HIDL" : "AIDL");
+        CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall())
+                << " before call " << getStackPointerDebugInfo();
         if (idx > 0) {
-            if (thisId == kP1Id && idx % 4 < 2) {
+            if (doCallHidl) {
                 callHidl(otherId, idx - 1);
             } else {
                 callAidl(otherId, idx - 1);
             }
         }
-        CHECK(BinderCallType::HWBINDER == getCurrentServingCall());
+        CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall())
+                << " after call " << getStackPointerDebugInfo();
         return android::hardware::Status::ok();
     }
 };
@@ -104,21 +122,24 @@
     size_t otherId;
 
     Status callLocal() {
-        CHECK(BinderCallType::NONE == getCurrentServingCall());
+        CHECK_EQ(BinderCallType::NONE, getCurrentServingCall());
         return Status::ok();
     }
     Status call(int32_t idx) {
+        bool doCallHidl = thisId == kP2Id && idx % 4 < 2;
         LOG(INFO) << "AidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx
-                  << " with tid: " << gettid();
-        CHECK(BinderCallType::BINDER == getCurrentServingCall());
+                  << " with tid: " << gettid() << " calling " << (doCallHidl ? "HIDL" : "AIDL");
+        CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall())
+                << " before call " << getStackPointerDebugInfo();
         if (idx > 0) {
-            if (thisId == kP2Id && idx % 4 < 2) {
+            if (doCallHidl) {
                 callHidl(otherId, idx - 1);
             } else {
                 callAidl(otherId, idx - 1);
             }
         }
-        CHECK(BinderCallType::BINDER == getCurrentServingCall());
+        CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall())
+                << " after call " << getStackPointerDebugInfo();
         return Status::ok();
     }
 };
@@ -161,13 +182,14 @@
     // AIDL
     android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
     sp<AidlServer> aidlServer = new AidlServer(thisId, otherId);
-    CHECK(OK == defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer));
+    CHECK_EQ(OK,
+             defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer));
     android::ProcessState::self()->startThreadPool();
 
     // HIDL
     android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
     sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
-    CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str()));
+    CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str()));
     android::hardware::joinRpcThreadpool();
 
     return EXIT_FAILURE;
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 47c0657..29924ff 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -28,6 +28,7 @@
 cc_library {
     name: "libfakeservicemanager",
     defaults: ["fakeservicemanager_defaults"],
+    export_include_dirs: ["include/fakeservicemanager"],
 }
 
 cc_test_host {
@@ -37,4 +38,5 @@
         "test_sm.cpp",
     ],
     static_libs: ["libgmock"],
+    local_include_dirs: ["include/fakeservicemanager"],
 }
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
similarity index 100%
rename from libs/fakeservicemanager/ServiceManager.h
rename to libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index c010a2e..c1945fd 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -14,12 +14,15 @@
         address: true,
     },
     srcs: [
+        "algorithm_test.cpp",
         "cast_test.cpp",
         "concat_test.cpp",
         "enum_test.cpp",
         "fake_guard_test.cpp",
         "flags_test.cpp",
         "future_test.cpp",
+        "match_test.cpp",
+        "optional_test.cpp",
         "small_map_test.cpp",
         "small_vector_test.cpp",
         "static_vector_test.cpp",
diff --git a/libs/ftl/algorithm_test.cpp b/libs/ftl/algorithm_test.cpp
new file mode 100644
index 0000000..8052caf
--- /dev/null
+++ b/libs/ftl/algorithm_test.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/algorithm.h>
+#include <ftl/small_map.h>
+#include <ftl/static_vector.h>
+#include <gtest/gtest.h>
+
+#include <string_view>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(Algorithm, FindIf) {
+  using namespace std::string_view_literals;
+
+  const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv};
+  EXPECT_EQ(ftl::find_if(vector, [](const auto& str) { return str.front() == 'c'; }), "cake"sv);
+
+  const ftl::SmallMap map = ftl::init::map<int, ftl::StaticVector<std::string_view, 3>>(
+      12, "snow"sv, "cone"sv)(13, "tiramisu"sv)(14, "upside"sv, "down"sv, "cake"sv);
+
+  using Map = decltype(map);
+
+  EXPECT_EQ(14, ftl::find_if(map, [](const auto& pair) {
+                  return pair.second.size() == 3;
+                }).transform(ftl::to_key<Map>));
+
+  const auto opt = ftl::find_if(map, [](const auto& pair) {
+                     return pair.second.size() == 1;
+                   }).transform(ftl::to_mapped_ref<Map>);
+
+  ASSERT_TRUE(opt);
+  EXPECT_EQ(opt->get(), ftl::StaticVector("tiramisu"sv));
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/match_test.cpp b/libs/ftl/match_test.cpp
new file mode 100644
index 0000000..a6cff2e
--- /dev/null
+++ b/libs/ftl/match_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/match.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <string>
+#include <variant>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(Match, Example) {
+  using namespace std::chrono;
+  using namespace std::chrono_literals;
+  using namespace std::string_literals;
+
+  std::variant<seconds, minutes, hours> duration = 119min;
+
+  // Mutable match.
+  ftl::match(duration, [](auto& d) { ++d; });
+
+  // Immutable match. Exhaustive due to minutes being convertible to seconds.
+  EXPECT_EQ("2 hours"s,
+            ftl::match(
+                duration,
+                [](const seconds& s) {
+                  const auto h = duration_cast<hours>(s);
+                  return std::to_string(h.count()) + " hours"s;
+                },
+                [](const hours& h) { return std::to_string(h.count() / 24) + " days"s; }));
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp
new file mode 100644
index 0000000..f7410c2
--- /dev/null
+++ b/libs/ftl/optional_test.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/optional.h>
+#include <ftl/static_vector.h>
+#include <ftl/string.h>
+#include <ftl/unit.h>
+#include <gtest/gtest.h>
+
+#include <cstdlib>
+#include <functional>
+#include <numeric>
+#include <utility>
+
+using namespace std::placeholders;
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::Optional;
+using ftl::StaticVector;
+
+TEST(Optional, Construct) {
+  // Empty.
+  EXPECT_EQ(std::nullopt, Optional<int>());
+  EXPECT_EQ(std::nullopt, Optional<std::string>(std::nullopt));
+
+  // Value.
+  EXPECT_EQ('?', Optional('?'));
+  EXPECT_EQ(""s, Optional(std::string()));
+
+  // In place.
+  EXPECT_EQ("???"s, Optional<std::string>(std::in_place, 3u, '?'));
+  EXPECT_EQ("abc"s, Optional<std::string>(std::in_place, {'a', 'b', 'c'}));
+
+  // Implicit downcast.
+  {
+    Optional opt = std::optional("test"s);
+    static_assert(std::is_same_v<decltype(opt), Optional<std::string>>);
+
+    ASSERT_TRUE(opt);
+    EXPECT_EQ(opt.value(), "test"s);
+  }
+}
+
+TEST(Optional, Transform) {
+  // Empty.
+  EXPECT_EQ(std::nullopt, Optional<int>().transform([](int) { return 0; }));
+
+  // By value.
+  EXPECT_EQ(0, Optional(0).transform([](int x) { return x; }));
+  EXPECT_EQ(100, Optional(99).transform([](int x) { return x + 1; }));
+  EXPECT_EQ("0b100"s, Optional(4).transform(std::bind(ftl::to_string<int>, _1, ftl::Radix::kBin)));
+
+  // By reference.
+  {
+    Optional opt = 'x';
+    EXPECT_EQ('z', opt.transform([](char& c) {
+      c = 'y';
+      return 'z';
+    }));
+
+    EXPECT_EQ('y', opt);
+  }
+
+  // By rvalue reference.
+  {
+    std::string out;
+    EXPECT_EQ("xyz"s, Optional("abc"s).transform([&out](std::string&& str) {
+      out = std::move(str);
+      return "xyz"s;
+    }));
+
+    EXPECT_EQ(out, "abc"s);
+  }
+
+  // No return value.
+  {
+    Optional opt = "food"s;
+    EXPECT_EQ(ftl::unit, opt.transform(ftl::unit_fn([](std::string& str) { str.pop_back(); })));
+    EXPECT_EQ(opt, "foo"s);
+  }
+
+  // Chaining.
+  EXPECT_EQ(14u, Optional(StaticVector{"upside"s, "down"s})
+                     .transform([](StaticVector<std::string, 3>&& v) {
+                       v.push_back("cake"s);
+                       return v;
+                     })
+                     .transform([](const StaticVector<std::string, 3>& v) {
+                       return std::accumulate(v.begin(), v.end(), std::string());
+                     })
+                     .transform([](const std::string& s) { return s.length(); }));
+}
+
+namespace {
+
+Optional<int> parse_int(const std::string& str) {
+  if (const int i = std::atoi(str.c_str())) return i;
+  return std::nullopt;
+}
+
+}  // namespace
+
+TEST(Optional, AndThen) {
+  // Empty.
+  EXPECT_EQ(std::nullopt, Optional<int>().and_then([](int) -> Optional<int> { return 0; }));
+  EXPECT_EQ(std::nullopt, Optional<int>().and_then([](int) { return Optional<int>(); }));
+
+  // By value.
+  EXPECT_EQ(0, Optional(0).and_then([](int x) { return Optional(x); }));
+  EXPECT_EQ(123, Optional("123").and_then(parse_int));
+  EXPECT_EQ(std::nullopt, Optional("abc").and_then(parse_int));
+
+  // By reference.
+  {
+    Optional opt = 'x';
+    EXPECT_EQ('z', opt.and_then([](char& c) {
+      c = 'y';
+      return Optional('z');
+    }));
+
+    EXPECT_EQ('y', opt);
+  }
+
+  // By rvalue reference.
+  {
+    std::string out;
+    EXPECT_EQ("xyz"s, Optional("abc"s).and_then([&out](std::string&& str) {
+      out = std::move(str);
+      return Optional("xyz"s);
+    }));
+
+    EXPECT_EQ(out, "abc"s);
+  }
+
+  // Chaining.
+  using StringVector = StaticVector<std::string, 3>;
+  EXPECT_EQ(14u, Optional(StaticVector{"-"s, "1"s})
+                     .and_then([](StringVector&& v) -> Optional<StringVector> {
+                       if (v.push_back("4"s)) return v;
+                       return {};
+                     })
+                     .and_then([](const StringVector& v) -> Optional<std::string> {
+                       if (v.full()) return std::accumulate(v.begin(), v.end(), std::string());
+                       return {};
+                     })
+                     .and_then(parse_int)
+                     .and_then([](int i) {
+                       return i > 0 ? std::nullopt : std::make_optional(static_cast<unsigned>(-i));
+                     }));
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
index 1740a2b..634877f 100644
--- a/libs/ftl/small_map_test.cpp
+++ b/libs/ftl/small_map_test.cpp
@@ -15,12 +15,15 @@
  */
 
 #include <ftl/small_map.h>
+#include <ftl/unit.h>
 #include <gtest/gtest.h>
 
 #include <cctype>
 #include <string>
+#include <string_view>
 
 using namespace std::string_literals;
+using namespace std::string_view_literals;
 
 namespace android::test {
 
@@ -38,7 +41,7 @@
 
   EXPECT_TRUE(map.contains(123));
 
-  EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u);
+  EXPECT_EQ(map.get(42).transform([](const std::string& s) { return s.size(); }), 3u);
 
   const auto opt = map.get(-1);
   ASSERT_TRUE(opt);
@@ -50,7 +53,7 @@
   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")));
+  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz"sv)(0, "nil"sv)(42, "???"sv)(123, "abc"sv)));
 }
 
 TEST(SmallMap, Construct) {
@@ -70,7 +73,7 @@
     EXPECT_EQ(map.max_size(), 5u);
     EXPECT_FALSE(map.dynamic());
 
-    EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi")));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc"sv)(456, "def"sv)(789, "ghi"sv)));
   }
   {
     // In-place constructor with different types.
@@ -81,7 +84,7 @@
     EXPECT_EQ(map.max_size(), 5u);
     EXPECT_FALSE(map.dynamic());
 
-    EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0")));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???"sv)(123, "abc"sv)(-1, ""sv)));
   }
   {
     // In-place constructor with implicit size.
@@ -92,7 +95,7 @@
     EXPECT_EQ(map.max_size(), 3u);
     EXPECT_FALSE(map.dynamic());
 
-    EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc")));
+    EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""sv)(42, "???"sv)(123, "abc"sv)));
   }
 }
 
@@ -108,7 +111,7 @@
   {
     // Convertible types; same capacity.
     SmallMap map1 = ftl::init::map<char, std::string>('M', "mega")('G', "giga");
-    const SmallMap map2 = ftl::init::map('T', "tera")('P', "peta");
+    const SmallMap map2 = ftl::init::map('T', "tera"sv)('P', "peta"sv);
 
     map1 = map2;
     EXPECT_EQ(map1, map2);
@@ -147,7 +150,7 @@
   }
 }
 
-TEST(SmallMap, Find) {
+TEST(SmallMap, Get) {
   {
     // Constant reference.
     const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
@@ -172,14 +175,15 @@
     EXPECT_EQ(d, 'D');
   }
   {
-    // Constant unary operation.
+    // Immutable transform operation.
     const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-    EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z');
+    EXPECT_EQ(map.get('c').transform([](char c) { return std::toupper(c); }), 'Z');
   }
   {
-    // Mutable unary operation.
+    // Mutable transform operation.
     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.get('c').transform(ftl::unit_fn([](char& c) { c = std::toupper(c); })),
+              ftl::unit);
 
     EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
   }
@@ -247,7 +251,7 @@
   }
   {
     // Replacement arguments can refer to the replaced mapping.
-    const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+    const auto ref = map.get(2).transform([](const String& s) { return s.str[0]; });
     ASSERT_TRUE(ref);
 
     // Construct std::string from one character.
@@ -292,7 +296,7 @@
   }
   {
     // Replacement arguments can refer to the replaced mapping.
-    const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+    const auto ref = map.get(2).transform([](const String& s) { return s.str[0]; });
     ASSERT_TRUE(ref);
 
     // Construct std::string from one character.
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index bd21fba..3d81c32 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -23,6 +23,7 @@
 
 cc_library {
     name: "libgralloctypes",
+    defaults: ["android.hardware.graphics.common-ndk_shared"],
     cflags: [
         "-Wall",
         "-Werror",
@@ -51,7 +52,6 @@
     ],
 
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
         "liblog",
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index e138212..23a2181 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -217,7 +217,6 @@
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
-        "TransactionTracing.cpp",
         "VsyncEventData.cpp",
         "view/Surface.cpp",
         "WindowInfosListenerReporter.cpp",
@@ -336,6 +335,8 @@
 cc_defaults {
     name: "libgui_bufferqueue-defaults",
 
+    defaults: ["android.hardware.graphics.common-ndk_shared"],
+
     cflags: [
         "-Wall",
         "-Werror",
@@ -364,7 +365,6 @@
         "android.hardware.graphics.bufferqueue@2.0",
         "android.hardware.graphics.common@1.1",
         "android.hardware.graphics.common@1.2",
-        "android.hardware.graphics.common-V3-ndk",
         "android.hidl.token@1.0-utils",
         "libbase",
         "libcutils",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3bf2e19..8b9a878 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -307,7 +307,6 @@
             BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
                      "empty.");
         }
-
         decStrong((void*)transactionCommittedCallbackThunk);
     }
 }
@@ -350,6 +349,20 @@
                                                     stat.latchTime,
                                                     stat.frameEventStats.dequeueReadyTime);
                 }
+                auto currFrameNumber = stat.frameEventStats.frameNumber;
+                std::vector<ReleaseCallbackId> staleReleases;
+                for (const auto& [key, value]: mSubmitted) {
+                    if (currFrameNumber > key.framenumber) {
+                        staleReleases.push_back(key);
+                    }
+                }
+                for (const auto& staleRelease : staleReleases) {
+                    BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback");
+                    BBQ_TRACE("FakeReleaseCallback");
+                    releaseBufferCallbackLocked(staleRelease,
+                        stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE,
+                        stat.currentMaxAcquiredBufferCount);
+                }
             } else {
                 BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
             }
@@ -390,7 +403,14 @@
         const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
         std::optional<uint32_t> currentMaxAcquiredBufferCount) {
     BBQ_TRACE();
+
     std::unique_lock _lock{mMutex};
+    releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount);
+}
+
+void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id,
+        const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount) {
+    ATRACE_CALL();
     BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
 
     // Calculate how many buffers we need to hold before we release them back
@@ -408,7 +428,11 @@
 
     const auto numPendingBuffersToHold =
             isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
-    mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
+
+    auto rb = ReleasedBuffer{id, releaseFence};
+    if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) {
+        mPendingRelease.emplace_back(rb);
+    }
 
     // Release all buffers that are beyond the ones that we need to hold
     while (mPendingRelease.size() > numPendingBuffersToHold) {
@@ -1083,50 +1107,6 @@
     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 */);
-        // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
-        t.setApplyToken(mApplyToken).apply(false, true);
-    }
-
-    // Clear sync states
-    if (!mSyncedFrameNumbers.empty()) {
-        BQA_LOGD("mSyncedFrameNumbers cleared");
-        mSyncedFrameNumbers.clear();
-    }
-
-    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 = nullptr;
-    mConsumer = nullptr;
-    mProducer = nullptr;
-}
-
 bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const {
     std::unique_lock _lock{mMutex};
     return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index af64b3b..4c887ec 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -19,7 +19,6 @@
 
 #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>
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index bb66085..924e65e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -40,15 +40,13 @@
         x(0),
         y(0),
         z(0),
-        w(0),
-        h(0),
-        alpha(0),
         flags(0),
         mask(0),
         reserved(0),
         cornerRadius(0.0f),
         backgroundBlurRadius(0),
-        transform(0),
+        color(0),
+        bufferTransform(0),
         transformToDisplayInverse(false),
         crop(Rect::INVALID_RECT),
         dataspace(ui::Dataspace::UNKNOWN),
@@ -84,23 +82,20 @@
     SAFE_PARCEL(output.writeFloat, x);
     SAFE_PARCEL(output.writeFloat, y);
     SAFE_PARCEL(output.writeInt32, z);
-    SAFE_PARCEL(output.writeUint32, w);
-    SAFE_PARCEL(output.writeUint32, h);
     SAFE_PARCEL(output.writeUint32, layerStack.id);
-    SAFE_PARCEL(output.writeFloat, alpha);
     SAFE_PARCEL(output.writeUint32, flags);
     SAFE_PARCEL(output.writeUint32, mask);
     SAFE_PARCEL(matrix.write, output);
     SAFE_PARCEL(output.write, crop);
-    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
     SAFE_PARCEL(output.writeFloat, color.r);
     SAFE_PARCEL(output.writeFloat, color.g);
     SAFE_PARCEL(output.writeFloat, color.b);
+    SAFE_PARCEL(output.writeFloat, color.a);
     SAFE_PARCEL(windowInfoHandle->writeToParcel, &output);
     SAFE_PARCEL(output.write, transparentRegion);
-    SAFE_PARCEL(output.writeUint32, transform);
+    SAFE_PARCEL(output.writeUint32, bufferTransform);
     SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
     SAFE_PARCEL(output.writeBool, borderEnabled);
     SAFE_PARCEL(output.writeFloat, borderWidth);
@@ -180,10 +175,7 @@
     SAFE_PARCEL(input.readFloat, &x);
     SAFE_PARCEL(input.readFloat, &y);
     SAFE_PARCEL(input.readInt32, &z);
-    SAFE_PARCEL(input.readUint32, &w);
-    SAFE_PARCEL(input.readUint32, &h);
     SAFE_PARCEL(input.readUint32, &layerStack.id);
-    SAFE_PARCEL(input.readFloat, &alpha);
 
     SAFE_PARCEL(input.readUint32, &flags);
 
@@ -191,7 +183,6 @@
 
     SAFE_PARCEL(matrix.read, input);
     SAFE_PARCEL(input.read, crop);
-    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
 
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
@@ -203,10 +194,13 @@
     color.g = tmpFloat;
     SAFE_PARCEL(input.readFloat, &tmpFloat);
     color.b = tmpFloat;
+    SAFE_PARCEL(input.readFloat, &tmpFloat);
+    color.a = tmpFloat;
+
     SAFE_PARCEL(windowInfoHandle->readFromParcel, &input);
 
     SAFE_PARCEL(input.read, transparentRegion);
-    SAFE_PARCEL(input.readUint32, &transform);
+    SAFE_PARCEL(input.readUint32, &bufferTransform);
     SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
     SAFE_PARCEL(input.readBool, &borderEnabled);
     SAFE_PARCEL(input.readFloat, &tmpFloat);
@@ -457,14 +451,9 @@
         what &= ~eRelativeLayerChanged;
         z = other.z;
     }
-    if (other.what & eSizeChanged) {
-        what |= eSizeChanged;
-        w = other.w;
-        h = other.h;
-    }
     if (other.what & eAlphaChanged) {
         what |= eAlphaChanged;
-        alpha = other.alpha;
+        color.a = other.color.a;
     }
     if (other.what & eMatrixChanged) {
         what |= eMatrixChanged;
@@ -506,12 +495,9 @@
         what |= eReparent;
         parentSurfaceControlForChild = other.parentSurfaceControlForChild;
     }
-    if (other.what & eDestroySurface) {
-        what |= eDestroySurface;
-    }
-    if (other.what & eTransformChanged) {
-        what |= eTransformChanged;
-        transform = other.transform;
+    if (other.what & eBufferTransformChanged) {
+        what |= eBufferTransformChanged;
+        bufferTransform = other.bufferTransform;
     }
     if (other.what & eTransformToDisplayInverseChanged) {
         what |= eTransformToDisplayInverseChanged;
@@ -558,7 +544,7 @@
     }
     if (other.what & eBackgroundColorChanged) {
         what |= eBackgroundColorChanged;
-        color = other.color;
+        color.rgb = other.color.rgb;
         bgColorAlpha = other.bgColorAlpha;
         bgColorDataspace = other.bgColorDataspace;
     }
@@ -623,7 +609,7 @@
     }
     if (other.what & eColorChanged) {
         what |= eColorChanged;
-        color = other.color;
+        color.rgb = other.color.rgb;
     }
     if (other.what & eColorSpaceAgnosticChanged) {
         what |= eColorSpaceAgnosticChanged;
@@ -645,7 +631,7 @@
 }
 
 bool layer_state_t::hasValidBuffer() const {
-    return bufferData && (bufferData->buffer || bufferData->cachedBuffer.isValid());
+    return bufferData && (bufferData->hasBuffer() || bufferData->cachedBuffer.isValid());
 }
 
 status_t layer_state_t::matrix22_t::write(Parcel& output) const {
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 31bf895..05b5533 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -2,8 +2,9 @@
 alecmouri@google.com
 chaviw@google.com
 chrisforbes@google.com
-lpy@google.com
 jreck@google.com
+lpy@google.com
+pdwilliams@google.com
 racarr@google.com
 vishnun@google.com
 
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
index fe38706..601a5f9 100644
--- a/libs/gui/ScreenCaptureResults.cpp
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -17,6 +17,7 @@
 #include <gui/ScreenCaptureResults.h>
 
 #include <private/gui/ParcelUtils.h>
+#include <ui/FenceResult.h>
 
 namespace android::gui {
 
@@ -28,17 +29,17 @@
         SAFE_PARCEL(parcel->writeBool, false);
     }
 
-    if (fence != Fence::NO_FENCE) {
+    if (fenceResult.ok() && fenceResult.value() != Fence::NO_FENCE) {
         SAFE_PARCEL(parcel->writeBool, true);
-        SAFE_PARCEL(parcel->write, *fence);
+        SAFE_PARCEL(parcel->write, *fenceResult.value());
     } else {
         SAFE_PARCEL(parcel->writeBool, false);
+        SAFE_PARCEL(parcel->writeInt32, fenceStatus(fenceResult));
     }
 
     SAFE_PARCEL(parcel->writeBool, capturedSecureLayers);
     SAFE_PARCEL(parcel->writeBool, capturedHdrLayers);
     SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace));
-    SAFE_PARCEL(parcel->writeInt32, result);
     return NO_ERROR;
 }
 
@@ -53,8 +54,13 @@
     bool hasFence;
     SAFE_PARCEL(parcel->readBool, &hasFence);
     if (hasFence) {
-        fence = new Fence();
-        SAFE_PARCEL(parcel->read, *fence);
+        fenceResult = sp<Fence>::make();
+        SAFE_PARCEL(parcel->read, *fenceResult.value());
+    } else {
+        status_t status;
+        SAFE_PARCEL(parcel->readInt32, &status);
+        fenceResult = status == NO_ERROR ? FenceResult(Fence::NO_FENCE)
+                                         : FenceResult(base::unexpected(status));
     }
 
     SAFE_PARCEL(parcel->readBool, &capturedSecureLayers);
@@ -62,7 +68,6 @@
     uint32_t dataspace = 0;
     SAFE_PARCEL(parcel->readUint32, &dataspace);
     capturedDataspace = static_cast<ui::Dataspace>(dataspace);
-    SAFE_PARCEL(parcel->readInt32, &result);
     return NO_ERROR;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 4b4d46a..c4fb1cf 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -56,6 +56,12 @@
 
 namespace {
 
+enum {
+    // moved from nativewindow/include/system/window.h, to be removed
+    NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
+    NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
+};
+
 bool isInterceptorRegistrationOp(int op) {
     return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR ||
             op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR ||
@@ -348,34 +354,25 @@
     return NO_ERROR;
 }
 
+// Deprecated(b/242763577): to be removed, this method should not be used
+// The reason this method still exists here is to support compiled vndk
+// Surface support should not be tied to the display
+// Return true since most displays should have this support
 status_t Surface::getWideColorSupport(bool* supported) {
     ATRACE_CALL();
 
-    const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
-    if (display == nullptr) {
-        return NAME_NOT_FOUND;
-    }
-
-    *supported = false;
-    binder::Status status = composerServiceAIDL()->isWideColorDisplay(display, supported);
-    return statusTFromBinderStatus(status);
+    *supported = true;
+    return NO_ERROR;
 }
 
+// Deprecated(b/242763577): to be removed, this method should not be used
+// The reason this method still exists here is to support compiled vndk
+// Surface support should not be tied to the display
+// Return true since most displays should have this support
 status_t Surface::getHdrSupport(bool* supported) {
     ATRACE_CALL();
 
-    const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
-    if (display == nullptr) {
-        return NAME_NOT_FOUND;
-    }
-
-    gui::DynamicDisplayInfo info;
-    if (binder::Status status = composerServiceAIDL()->getDynamicDisplayInfo(display, &info);
-        !status.isOk()) {
-        return statusTFromBinderStatus(status);
-    }
-
-    *supported = !info.hdrCapabilities.supportedHdrTypes.empty();
+    *supported = true;
     return NO_ERROR;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index d16ddb9..04d4a07 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "SurfaceComposerClient"
 
+#include <semaphore.h>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -24,6 +25,7 @@
 #include <android/gui/ISurfaceComposerClient.h>
 #include <android/gui/IWindowInfosListener.h>
 #include <android/os/IInputConstants.h>
+#include <gui/TraceUtils.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/SortedVector.h>
@@ -356,7 +358,8 @@
                                       transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
                                       transactionStats.presentFence,
                                       surfaceStats.previousReleaseFence, surfaceStats.transformHint,
-                                      surfaceStats.eventStats);
+                                      surfaceStats.eventStats,
+                                      surfaceStats.currentMaxAcquiredBufferCount);
             }
 
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -381,7 +384,8 @@
                                       transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
                                       transactionStats.presentFence,
                                       surfaceStats.previousReleaseFence, surfaceStats.transformHint,
-                                      surfaceStats.eventStats);
+                                      surfaceStats.eventStats,
+                                      surfaceStats.currentMaxAcquiredBufferCount);
                 if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
                     callbacksMap[callbackId]
                             .surfaceControls[surfaceStats.surfaceControl]
@@ -627,12 +631,11 @@
 
 SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
       : mId(other.mId),
-        mForceSynchronous(other.mForceSynchronous),
         mTransactionNestCount(other.mTransactionNestCount),
         mAnimation(other.mAnimation),
         mEarlyWakeupStart(other.mEarlyWakeupStart),
         mEarlyWakeupEnd(other.mEarlyWakeupEnd),
-        mContainsBuffer(other.mContainsBuffer),
+        mMayContainBuffer(other.mMayContainBuffer),
         mDesiredPresentTime(other.mDesiredPresentTime),
         mIsAutoTimestamp(other.mIsAutoTimestamp),
         mFrameTimelineInfo(other.mFrameTimelineInfo),
@@ -662,12 +665,10 @@
 
 status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
     const uint64_t transactionId = parcel->readUint64();
-    const uint32_t forceSynchronous = parcel->readUint32();
     const uint32_t transactionNestCount = parcel->readUint32();
     const bool animation = parcel->readBool();
     const bool earlyWakeupStart = parcel->readBool();
     const bool earlyWakeupEnd = parcel->readBool();
-    const bool containsBuffer = parcel->readBool();
     const int64_t desiredPresentTime = parcel->readInt64();
     const bool isAutoTimestamp = parcel->readBool();
     FrameTimelineInfo frameTimelineInfo;
@@ -740,12 +741,10 @@
 
     // Parsing was successful. Update the object.
     mId = transactionId;
-    mForceSynchronous = forceSynchronous;
     mTransactionNestCount = transactionNestCount;
     mAnimation = animation;
     mEarlyWakeupStart = earlyWakeupStart;
     mEarlyWakeupEnd = earlyWakeupEnd;
-    mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
     mIsAutoTimestamp = isAutoTimestamp;
     mFrameTimelineInfo = frameTimelineInfo;
@@ -772,12 +771,10 @@
     const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
 
     parcel->writeUint64(mId);
-    parcel->writeUint32(mForceSynchronous);
     parcel->writeUint32(mTransactionNestCount);
     parcel->writeBool(mAnimation);
     parcel->writeBool(mEarlyWakeupStart);
     parcel->writeBool(mEarlyWakeupEnd);
-    parcel->writeBool(mContainsBuffer);
     parcel->writeInt64(mDesiredPresentTime);
     parcel->writeBool(mIsAutoTimestamp);
     mFrameTimelineInfo.writeToParcel(parcel);
@@ -876,7 +873,7 @@
 
     mInputWindowCommands.merge(other.mInputWindowCommands);
 
-    mContainsBuffer |= other.mContainsBuffer;
+    mMayContainBuffer |= other.mMayContainBuffer;
     mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
     mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
     mApplyToken = other.mApplyToken;
@@ -892,8 +889,7 @@
     mDisplayStates.clear();
     mListenerCallbacks.clear();
     mInputWindowCommands.clear();
-    mContainsBuffer = false;
-    mForceSynchronous = 0;
+    mMayContainBuffer = false;
     mTransactionNestCount = 0;
     mAnimation = false;
     mEarlyWakeupStart = false;
@@ -915,13 +911,18 @@
     uncacheBuffer.token = BufferCache::getInstance().getToken();
     uncacheBuffer.id = cacheId;
 
-    sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, applyToken, {}, systemTime(), true,
-                            uncacheBuffer, false, {}, generateId());
+    status_t status =
+            sf->setTransactionState(FrameTimelineInfo{}, {}, {}, ISurfaceComposer::eOneWay,
+                                    Transaction::getDefaultApplyToken(), {}, systemTime(), true,
+                                    uncacheBuffer, false, {}, generateId());
+    if (status != NO_ERROR) {
+        ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
+                        strerror(-status));
+    }
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
-    if (!mContainsBuffer) {
+    if (!mMayContainBuffer) {
         return;
     }
 
@@ -966,12 +967,55 @@
     }
 }
 
+class SyncCallback {
+public:
+    static void function(void* callbackContext, nsecs_t /* latchTime */,
+                         const sp<Fence>& /* presentFence */,
+                         const std::vector<SurfaceControlStats>& /* stats */) {
+        if (!callbackContext) {
+            ALOGE("failed to get callback context for SyncCallback");
+        }
+        SyncCallback* helper = static_cast<SyncCallback*>(callbackContext);
+        LOG_ALWAYS_FATAL_IF(sem_post(&helper->mSemaphore), "sem_post failed");
+    }
+    ~SyncCallback() {
+        if (mInitialized) {
+            LOG_ALWAYS_FATAL_IF(sem_destroy(&mSemaphore), "sem_destroy failed");
+        }
+    }
+    void init() {
+        LOG_ALWAYS_FATAL_IF(clock_gettime(CLOCK_MONOTONIC, &mTimeoutTimespec) == -1,
+                            "clock_gettime() fail! in SyncCallback::init");
+        mTimeoutTimespec.tv_sec += 4;
+        LOG_ALWAYS_FATAL_IF(sem_init(&mSemaphore, 0, 0), "sem_init failed");
+        mInitialized = true;
+    }
+    void wait() {
+        int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &mTimeoutTimespec);
+        if (result && errno != ETIMEDOUT && errno != EINTR) {
+            LOG_ALWAYS_FATAL("sem_clockwait failed(%d)", errno);
+        } else if (errno == ETIMEDOUT) {
+            ALOGW("Sync transaction timed out waiting for commit callback.");
+        }
+    }
+    void* getContext() { return static_cast<void*>(this); }
+
+private:
+    sem_t mSemaphore;
+    bool mInitialized = false;
+    timespec mTimeoutTimespec;
+};
+
 status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
     if (mStatus != NO_ERROR) {
         return mStatus;
     }
 
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    SyncCallback syncCallback;
+    if (synchronous) {
+        syncCallback.init();
+        addTransactionCommittedCallback(syncCallback.function, syncCallback.getContext());
+    }
 
     bool hasListenerCallbacks = !mListenerCallbacks.empty();
     std::vector<ListenerCallbacks> listenerCallbacks;
@@ -1006,27 +1050,22 @@
     Vector<DisplayState> displayStates;
     uint32_t flags = 0;
 
-    mForceSynchronous |= synchronous;
-
-    for (auto const& kv : mComposerStates){
+    for (auto const& kv : mComposerStates) {
         composerStates.add(kv.second);
     }
 
     displayStates = std::move(mDisplayStates);
 
-    if (mForceSynchronous) {
-        flags |= ISurfaceComposer::eSynchronous;
-    }
     if (mAnimation) {
         flags |= ISurfaceComposer::eAnimation;
     }
     if (oneWay) {
-      if (mForceSynchronous) {
-          ALOGE("Transaction attempted to set synchronous and one way at the same time"
-                " this is an invalid request. Synchronous will win for safety");
-      } else {
-          flags |= ISurfaceComposer::eOneWay;
-      }
+        if (synchronous) {
+            ALOGE("Transaction attempted to set synchronous and one way at the same time"
+                  " this is an invalid request. Synchronous will win for safety");
+        } else {
+            flags |= ISurfaceComposer::eOneWay;
+        }
     }
 
     // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
@@ -1038,10 +1077,9 @@
         flags |= ISurfaceComposer::eEarlyWakeupEnd;
     }
 
-    sp<IBinder> applyToken = mApplyToken
-            ? mApplyToken
-            : IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    sp<IBinder> applyToken = mApplyToken ? mApplyToken : sApplyToken;
 
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
                             mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
                             {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
@@ -1051,10 +1089,23 @@
     // Clear the current states and flags
     clear();
 
+    if (synchronous) {
+        syncCallback.wait();
+    }
+
     mStatus = NO_ERROR;
     return NO_ERROR;
 }
 
+sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder();
+
+sp<IBinder> SurfaceComposerClient::Transaction::getDefaultApplyToken() {
+    return sApplyToken;
+}
+
+void SurfaceComposerClient::Transaction::setDefaultApplyToken(sp<IBinder> applyToken) {
+    sApplyToken = applyToken;
+}
 // ---------------------------------------------------------------------------
 
 sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, bool secure) {
@@ -1085,11 +1136,6 @@
     return physicalDisplayIds;
 }
 
-std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() {
-    ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance();
-    return instance.getInternalDisplayId();
-}
-
 sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId displayId) {
     sp<IBinder> display = nullptr;
     binder::Status status =
@@ -1098,11 +1144,6 @@
     return status.isOk() ? display : nullptr;
 }
 
-sp<IBinder> SurfaceComposerClient::getInternalDisplayToken() {
-    ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance();
-    return instance.getInternalDisplayToken();
-}
-
 void SurfaceComposerClient::Transaction::setAnimationTransaction() {
     mAnimation = true;
 }
@@ -1165,21 +1206,6 @@
     return setFlags(sc, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSize(
-        const sp<SurfaceControl>& sc, uint32_t w, uint32_t h) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-    s->what |= layer_state_t::eSizeChanged;
-    s->w = w;
-    s->h = h;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer(
         const sp<SurfaceControl>& sc, int32_t z) {
     layer_state_t* s = getLayerState(sc);
@@ -1270,13 +1296,10 @@
         return *this;
     }
     if (alpha < 0.0f || alpha > 1.0f) {
-        ALOGE("SurfaceComposerClient::Transaction::setAlpha: invalid alpha %f", alpha);
-        mStatus = BAD_VALUE;
-        return *this;
-
+        ALOGE("SurfaceComposerClient::Transaction::setAlpha: invalid alpha %f, clamping", alpha);
     }
     s->what |= layer_state_t::eAlphaChanged;
-    s->alpha = alpha;
+    s->color.a = std::clamp(alpha, 0.f, 1.f);
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1407,7 +1430,7 @@
         return *this;
     }
     s->what |= layer_state_t::eColorChanged;
-    s->color = color;
+    s->color.rgb = color;
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1422,7 +1445,7 @@
     }
 
     s->what |= layer_state_t::eBackgroundColorChanged;
-    s->color = color;
+    s->color.rgb = color;
     s->bgColorAlpha = alpha;
     s->bgColorDataspace = dataspace;
 
@@ -1437,8 +1460,8 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eTransformChanged;
-    s->transform = transform;
+    s->what |= layer_state_t::eBufferTransformChanged;
+    s->bufferTransform = transform;
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1476,7 +1499,6 @@
     s->what &= ~layer_state_t::eBufferChanged;
     s->bufferData = nullptr;
 
-    mContainsBuffer = false;
     return bufferData;
 }
 
@@ -1507,7 +1529,6 @@
     if (buffer == nullptr) {
         s->what &= ~layer_state_t::eBufferChanged;
         s->bufferData = nullptr;
-        mContainsBuffer = false;
         return *this;
     }
 
@@ -1542,7 +1563,7 @@
                                        const std::vector<SurfaceControlStats>&) {},
                                     nullptr);
 
-    mContainsBuffer = true;
+    mMayContainBuffer = true;
     return *this;
 }
 
@@ -2037,7 +2058,6 @@
     s.layerStackSpaceRect = layerStackRect;
     s.orientedDisplaySpaceRect = displayRect;
     s.what |= DisplayState::eDisplayProjectionChanged;
-    mForceSynchronous = true; // TODO: do we actually still need this?
 }
 
 void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height) {
@@ -2132,6 +2152,10 @@
     return s;
 }
 
+static std::string toString(const String16& string) {
+    return std::string(String8(string).c_str());
+}
+
 status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                      PixelFormat format,
                                                      sp<SurfaceControl>* outSurface, int32_t flags,
@@ -2151,7 +2175,8 @@
         }
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
-            *outSurface = new SurfaceControl(this, result.handle, result.layerId, w, h, format,
+            *outSurface = new SurfaceControl(this, result.handle, result.layerId,
+                                             toString(result.layerName), w, h, format,
                                              result.transformHint, flags);
         }
     }
@@ -2164,21 +2189,21 @@
     }
 
     sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle();
-    gui::MirrorSurfaceResult result;
+    gui::CreateSurfaceResult result;
     const binder::Status status = mClient->mirrorSurface(mirrorFromHandle, &result);
     const status_t err = statusTFromBinderStatus(status);
     if (err == NO_ERROR) {
-        return new SurfaceControl(this, result.handle, result.layerId);
+        return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
     }
     return nullptr;
 }
 
 sp<SurfaceControl> SurfaceComposerClient::mirrorDisplay(DisplayId displayId) {
-    gui::MirrorSurfaceResult result;
+    gui::CreateSurfaceResult result;
     const binder::Status status = mClient->mirrorDisplay(displayId.value, &result);
     const status_t err = statusTFromBinderStatus(status);
     if (err == NO_ERROR) {
-        return new SurfaceControl(this, result.handle, result.layerId);
+        return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
     }
     return nullptr;
 }
@@ -2218,18 +2243,6 @@
 
 // ----------------------------------------------------------------------------
 
-status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
-    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-    binder::Status status = sf->enableVSyncInjections(enable);
-    return statusTFromBinderStatus(status);
-}
-
-status_t SurfaceComposerClient::injectVSync(nsecs_t when) {
-    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-    binder::Status status = sf->injectVSync(when);
-    return statusTFromBinderStatus(status);
-}
-
 status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display,
                                                 ui::DisplayState* state) {
     gui::DisplayState ds;
@@ -2727,11 +2740,14 @@
                                                                                    &gsupport);
     std::optional<DisplayDecorationSupport> support;
     if (status.isOk() && gsupport.has_value()) {
-        support->format = static_cast<aidl::android::hardware::graphics::common::PixelFormat>(
-                gsupport->format);
-        support->alphaInterpretation =
+        support.emplace(DisplayDecorationSupport{
+          .format =
+                static_cast<aidl::android::hardware::graphics::common::PixelFormat>(
+                gsupport->format),
+          .alphaInterpretation =
                 static_cast<aidl::android::hardware::graphics::common::AlphaInterpretation>(
-                        gsupport->alphaInterpretation);
+                        gsupport->alphaInterpretation)
+        });
     }
     return support;
 }
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 84257de..7aee882 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -49,11 +49,12 @@
 // ============================================================================
 
 SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
-                               int32_t layerId, uint32_t w, uint32_t h, PixelFormat format,
-                               uint32_t transform, uint32_t flags)
+                               int32_t layerId, const std::string& name, uint32_t w, uint32_t h,
+                               PixelFormat format, uint32_t transform, uint32_t flags)
       : mClient(client),
         mHandle(handle),
         mLayerId(layerId),
+        mName(name),
         mTransformHint(transform),
         mWidth(w),
         mHeight(h),
@@ -65,6 +66,7 @@
     mHandle = other->mHandle;
     mTransformHint = other->mTransformHint;
     mLayerId = other->mLayerId;
+    mName = other->mName;
     mWidth = other->mWidth;
     mHeight = other->mHeight;
     mFormat = other->mFormat;
@@ -185,6 +187,10 @@
     return mLayerId;
 }
 
+const std::string& SurfaceControl::getName() const {
+    return mName;
+}
+
 sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer()
 {
     getSurface();
@@ -212,6 +218,7 @@
     SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient()));
     SAFE_PARCEL(parcel.writeStrongBinder, mHandle);
     SAFE_PARCEL(parcel.writeInt32, mLayerId);
+    SAFE_PARCEL(parcel.writeUtf8AsUtf16, mName);
     SAFE_PARCEL(parcel.writeUint32, mTransformHint);
     SAFE_PARCEL(parcel.writeUint32, mWidth);
     SAFE_PARCEL(parcel.writeUint32, mHeight);
@@ -225,6 +232,7 @@
     sp<IBinder> client;
     sp<IBinder> handle;
     int32_t layerId;
+    std::string layerName;
     uint32_t transformHint;
     uint32_t width;
     uint32_t height;
@@ -233,16 +241,17 @@
     SAFE_PARCEL(parcel.readStrongBinder, &client);
     SAFE_PARCEL(parcel.readStrongBinder, &handle);
     SAFE_PARCEL(parcel.readInt32, &layerId);
+    SAFE_PARCEL(parcel.readUtf8FromUtf16, &layerName);
     SAFE_PARCEL(parcel.readUint32, &transformHint);
     SAFE_PARCEL(parcel.readUint32, &width);
     SAFE_PARCEL(parcel.readUint32, &height);
     SAFE_PARCEL(parcel.readUint32, &format);
 
     // We aren't the original owner of the surface.
-    *outSurfaceControl =
-            new SurfaceControl(new SurfaceComposerClient(
-                                       interface_cast<ISurfaceComposerClient>(client)),
-                               handle.get(), layerId, width, height, format, transformHint);
+    *outSurfaceControl = new SurfaceControl(new SurfaceComposerClient(
+                                                    interface_cast<ISurfaceComposerClient>(client)),
+                                            handle.get(), layerId, layerName, width, height, format,
+                                            transformHint);
 
     return NO_ERROR;
 }
diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp
deleted file mode 100644
index 59450fb..0000000
--- a/libs/gui/TransactionTracing.cpp
+++ /dev/null
@@ -1,53 +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 "gui/TransactionTracing.h"
-#include "android/gui/ISurfaceComposer.h"
-
-#include <private/gui/ComposerServiceAIDL.h>
-
-namespace android {
-
-sp<TransactionTraceListener> TransactionTraceListener::sInstance = nullptr;
-std::mutex TransactionTraceListener::sMutex;
-
-TransactionTraceListener::TransactionTraceListener() {}
-
-sp<TransactionTraceListener> TransactionTraceListener::getInstance() {
-    const std::lock_guard<std::mutex> lock(sMutex);
-
-    if (sInstance == nullptr) {
-        sInstance = new TransactionTraceListener;
-
-        sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-        sf->addTransactionTraceListener(sInstance);
-    }
-
-    return sInstance;
-}
-
-binder::Status TransactionTraceListener::onToggled(bool enabled) {
-    ALOGD("TransactionTraceListener: onToggled listener called");
-    mTracingEnabled = enabled;
-
-    return binder::Status::ok();
-}
-
-bool TransactionTraceListener::isTracingEnabled() {
-    return mTracingEnabled;
-}
-
-} // namespace android
diff --git a/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl b/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl
index 39e4916..eea12dc 100644
--- a/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl
+++ b/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl
@@ -20,5 +20,6 @@
 parcelable CreateSurfaceResult {
     IBinder handle;
     int layerId;
+    String layerName;
     int transformHint;
 }
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 3c220fc..ca0b97f 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -36,7 +36,6 @@
 import android.gui.IRegionSamplingListener;
 import android.gui.IScreenCaptureListener;
 import android.gui.ISurfaceComposerClient;
-import android.gui.ITransactionTraceListener;
 import android.gui.ITunnelModeEnabledListener;
 import android.gui.IWindowInfosListener;
 import android.gui.LayerCaptureArgs;
@@ -225,10 +224,6 @@
      */
     PullAtomData onPullAtom(int atomId);
 
-    oneway void enableVSyncInjections(boolean enable);
-
-    oneway void injectVSync(long when);
-
     /**
      * Gets the list of active layers in Z order for debugging purposes
      *
@@ -453,11 +448,6 @@
     void setOverrideFrameRate(int uid, float frameRate);
 
     /**
-     * Adds a TransactionTraceListener to listen for transaction tracing state updates.
-     */
-    void addTransactionTraceListener(ITransactionTraceListener listener);
-
-    /**
      * Gets priority of the RenderEngine in SurfaceFlinger.
      */
     int getGpuContextPriority();
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
index b8ee4d7..68781ce 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
@@ -19,7 +19,6 @@
 import android.gui.CreateSurfaceResult;
 import android.gui.FrameStats;
 import android.gui.LayerMetadata;
-import android.gui.MirrorSurfaceResult;
 
 /** @hide */
 interface ISurfaceComposerClient {
@@ -58,7 +57,7 @@
      */
     FrameStats getLayerFrameStats(IBinder handle);
 
-    MirrorSurfaceResult mirrorSurface(IBinder mirrorFromHandle);
+    CreateSurfaceResult mirrorSurface(IBinder mirrorFromHandle);
 
-    MirrorSurfaceResult mirrorDisplay(long displayId);
+    CreateSurfaceResult mirrorDisplay(long displayId);
 }
diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
deleted file mode 100644
index 5cd12fd..0000000
--- a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
+++ /dev/null
@@ -1,6 +0,0 @@
-package android.gui;
-
-/** @hide */
-interface ITransactionTraceListener {
-   void onToggled(boolean enabled);
-}
\ No newline at end of file
diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
index c97770b..761f08f 100644
--- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <android-base/stringprintf.h>
 #include <gui/BufferQueueConsumer.h>
 #include <gui/BufferQueueCore.h>
 #include <gui/BufferQueueProducer.h>
@@ -91,8 +92,10 @@
     const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient());
     sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient);
     sp<BnGraphicBufferProducer> producer;
-    return sp<SurfaceControl>::make(client, handle, mFdp.ConsumeIntegral<int32_t>(),
-                                    mFdp.ConsumeIntegral<uint32_t>(),
+    uint32_t layerId = mFdp.ConsumeIntegral<uint32_t>();
+    std::string layerName = base::StringPrintf("#%d", layerId);
+    return sp<SurfaceControl>::make(client, handle, layerId, layerName,
+                                    mFdp.ConsumeIntegral<int32_t>(),
                                     mFdp.ConsumeIntegral<uint32_t>(),
                                     mFdp.ConsumeIntegral<int32_t>(),
                                     mFdp.ConsumeIntegral<uint32_t>(),
@@ -145,7 +148,8 @@
     sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING));
     SurfaceControlStats controlStats(surface, mFdp.ConsumeIntegral<int64_t>(),
                                      mFdp.ConsumeIntegral<int64_t>(), presentFence, previousFence,
-                                     mFdp.ConsumeIntegral<uint32_t>(), frameStats);
+                                     mFdp.ConsumeIntegral<uint32_t>(), frameStats,
+                                     mFdp.ConsumeIntegral<uint32_t>());
     stats.push_back(controlStats);
 }
 
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index d51f685..315b925 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -102,8 +102,6 @@
     MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&),
                 (override));
     MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override));
-    MOCK_METHOD(binder::Status, enableVSyncInjections, (bool), (override));
-    MOCK_METHOD(binder::Status, injectVSync, (int64_t), (override));
     MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override));
     MOCK_METHOD(binder::Status, getColorManagement, (bool*), (override));
     MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*),
@@ -148,8 +146,6 @@
     MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
                 (const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
     MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
-    MOCK_METHOD(binder::Status, addTransactionTraceListener,
-                (const sp<gui::ITransactionTraceListener>&), (override));
     MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
     MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
     MOCK_METHOD(binder::Status, addWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
@@ -171,11 +167,11 @@
                 (const sp<IBinder>& handle, gui::FrameStats* outStats), (override));
 
     MOCK_METHOD(binder::Status, mirrorSurface,
-                (const sp<IBinder>& mirrorFromHandle, gui::MirrorSurfaceResult* outResult),
+                (const sp<IBinder>& mirrorFromHandle, gui::CreateSurfaceResult* outResult),
                 (override));
 
     MOCK_METHOD(binder::Status, mirrorDisplay,
-                (int64_t displayId, gui::MirrorSurfaceResult* outResult), (override));
+                (int64_t displayId, gui::CreateSurfaceResult* outResult), (override));
 };
 
 class FakeDisplayEventDispatcher : public DisplayEventDispatcher {
diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
index 05564e0..eecbe0f 100644
--- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
@@ -18,6 +18,7 @@
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <libgui_fuzzer_utils.h>
+#include "android-base/stringprintf.h"
 
 using namespace android;
 
@@ -175,15 +176,15 @@
     uint32_t flags = mFdp.ConsumeIntegral<uint32_t>();
     int32_t format = mFdp.ConsumeIntegral<int32_t>();
     int32_t layerId = mFdp.ConsumeIntegral<int32_t>();
-    return new SurfaceControl(client, handle, layerId, width, height, format, transformHint, flags);
+    std::string layerName = base::StringPrintf("#%d", layerId);
+    return new SurfaceControl(client, handle, layerId, layerName, width, height, format,
+                              transformHint, flags);
 }
 
 void SurfaceComposerClientFuzzer::invokeSurfaceComposerTransaction() {
     sp<SurfaceControl> surface = makeSurfaceControl();
 
     SurfaceComposerClient::Transaction transaction;
-    transaction.setSize(surface, mFdp.ConsumeIntegral<uint32_t>(),
-                        mFdp.ConsumeIntegral<uint32_t>());
     int32_t layer = mFdp.ConsumeIntegral<int32_t>();
     transaction.setLayer(surface, layer);
 
@@ -270,10 +271,6 @@
     sp<Surface> surfaceParent(
             new Surface(producer, mFdp.ConsumeBool() /*controlledByApp*/, handle));
 
-    SurfaceComposerClient::enableVSyncInjections(mFdp.ConsumeBool() /*secure*/);
-    nsecs_t when = mFdp.ConsumeIntegral<uint32_t>();
-    SurfaceComposerClient::injectVSync(when);
-
     fuzzOnPullAtom();
     SurfaceComposerClient::setDisplayContentSamplingEnabled(displayToken,
                                                             mFdp.ConsumeBool() /*enable*/,
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 95df811..827a6cc 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -92,9 +92,12 @@
                                      const std::vector<SurfaceControlStats>& stats);
     void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
                                std::optional<uint32_t> currentMaxAcquiredBufferCount);
+    void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+                               std::optional<uint32_t> currentMaxAcquiredBufferCount);
     void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
                              bool acquireSingleBuffer = true);
     void stopContinuousSyncTransaction();
+
     void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
     void applyPendingTransactions(uint64_t frameNumber);
     SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber);
@@ -108,7 +111,6 @@
 
     uint32_t getLastTransformHint() const;
     uint64_t getLastAcquiredFrameNum();
-    void abandon();
 
     /**
      * Set a callback to be invoked when we are hung. The boolean parameter
@@ -174,6 +176,12 @@
     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);
 
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 1e85131..e91d754 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -23,7 +23,6 @@
 #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>
 #include <android/gui/IWindowInfosListener.h>
 #include <binder/IBinder.h>
@@ -95,7 +94,6 @@
 
     // flags for setTransactionState()
     enum {
-        eSynchronous = 0x01,
         eAnimation = 0x02,
 
         // Explicit indication that this transaction and others to follow will likely result in a
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 759fcc6..45272e7 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -133,7 +133,7 @@
         eLayerOpaque = 0x02,         // SURFACE_OPAQUE
         eLayerSkipScreenshot = 0x40, // SKIP_SCREENSHOT
         eLayerSecure = 0x80,         // SECURE
-        // Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is
+        // Queue up layer buffers instead of dropping the oldest buffer when this flag is
         // 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
@@ -148,12 +148,14 @@
     enum {
         ePositionChanged = 0x00000001,
         eLayerChanged = 0x00000002,
-        eSizeChanged = 0x00000004,
+        /* unused = 0x00000004, */
         eAlphaChanged = 0x00000008,
         eMatrixChanged = 0x00000010,
         eTransparentRegionChanged = 0x00000020,
         eFlagsChanged = 0x00000040,
         eLayerStackChanged = 0x00000080,
+        /* unused = 0x00000100, */
+        /* unused = 0x00000200, */
         eDimmingEnabledChanged = 0x00000400,
         eShadowRadiusChanged = 0x00000800,
         eRenderBorderChanged = 0x00001000,
@@ -161,8 +163,8 @@
         eRelativeLayerChanged = 0x00004000,
         eReparent = 0x00008000,
         eColorChanged = 0x00010000,
-        eDestroySurface = 0x00020000,
-        eTransformChanged = 0x00040000,
+        /* unused = 0x00020000, */
+        eBufferTransformChanged = 0x00040000,
         eTransformToDisplayInverseChanged = 0x00080000,
         eCropChanged = 0x00100000,
         eBufferChanged = 0x00200000,
@@ -217,28 +219,23 @@
     float x;
     float y;
     int32_t z;
-    uint32_t w;
-    uint32_t h;
     ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
-    float alpha;
     uint32_t flags;
     uint32_t mask;
     uint8_t reserved;
     matrix22_t matrix;
     float cornerRadius;
     uint32_t backgroundBlurRadius;
-    sp<SurfaceControl> reparentSurfaceControl;
 
     sp<SurfaceControl> relativeLayerSurfaceControl;
 
     sp<SurfaceControl> parentSurfaceControlForChild;
 
-    half3 color;
+    half4 color;
 
     // non POD must be last. see write/read
     Region transparentRegion;
-
-    uint32_t transform;
+    uint32_t bufferTransform;
     bool transformToDisplayInverse;
     Rect crop;
     std::shared_ptr<BufferData> bufferData = nullptr;
diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h
index 724c11c..6e17791 100644
--- a/libs/gui/include/gui/ScreenCaptureResults.h
+++ b/libs/gui/include/gui/ScreenCaptureResults.h
@@ -19,6 +19,7 @@
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 #include <ui/Fence.h>
+#include <ui/FenceResult.h>
 #include <ui/GraphicBuffer.h>
 
 namespace android::gui {
@@ -31,11 +32,10 @@
     status_t readFromParcel(const android::Parcel* parcel) override;
 
     sp<GraphicBuffer> buffer;
-    sp<Fence> fence = Fence::NO_FENCE;
+    FenceResult fenceResult = Fence::NO_FENCE;
     bool capturedSecureLayers{false};
     bool capturedHdrLayers{false};
     ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
-    status_t result = OK;
 };
 
 } // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 634acb6..1f19f4e 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -187,8 +187,8 @@
             nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
             nsecs_t* outReleaseTime);
 
-    status_t getWideColorSupport(bool* supported);
-    status_t getHdrSupport(bool* supported);
+    status_t getWideColorSupport(bool* supported) __attribute__((__deprecated__));
+    status_t getHdrSupport(bool* supported) __attribute__((__deprecated__));
 
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 20c38d8..d138d68 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -69,14 +69,16 @@
     SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
                         std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
                         const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
-                        uint32_t hint, FrameEventHistoryStats eventStats)
+                        uint32_t hint, FrameEventHistoryStats eventStats,
+                        uint32_t currentMaxAcquiredBufferCount)
           : surfaceControl(sc),
             latchTime(latchTime),
             acquireTimeOrFence(std::move(acquireTimeOrFence)),
             presentFence(presentFence),
             previousReleaseFence(prevReleaseFence),
             transformHint(hint),
-            frameEventStats(eventStats) {}
+            frameEventStats(eventStats),
+            currentMaxAcquiredBufferCount(currentMaxAcquiredBufferCount) {}
 
     sp<SurfaceControl> surfaceControl;
     nsecs_t latchTime = -1;
@@ -85,6 +87,7 @@
     sp<Fence> previousReleaseFence;
     uint32_t transformHint = 0;
     FrameEventHistoryStats frameEventStats;
+    uint32_t currentMaxAcquiredBufferCount = 0;
 };
 
 using TransactionCompletedCallbackTakesContext =
@@ -351,15 +354,9 @@
 
     //! Get stable IDs for connected physical displays
     static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
-    static std::optional<PhysicalDisplayId> getInternalDisplayId();
 
     //! Get token for a physical display given its stable ID
     static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
-    static sp<IBinder> getInternalDisplayToken();
-
-    static status_t enableVSyncInjections(bool enable);
-
-    static status_t injectVSync(nsecs_t when);
 
     struct SCHash {
         std::size_t operator()(const sp<SurfaceControl>& sc) const {
@@ -390,6 +387,7 @@
 
     class Transaction : public Parcelable {
     private:
+        static sp<IBinder> sApplyToken;
         void releaseBufferIfOverwriting(const layer_state_t& state);
         static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
         static void clearFrameTimelineInfo(FrameTimelineInfo& t);
@@ -402,14 +400,15 @@
 
         uint64_t mId;
 
-        uint32_t mForceSynchronous = 0;
         uint32_t mTransactionNestCount = 0;
         bool mAnimation = false;
         bool mEarlyWakeupStart = false;
         bool mEarlyWakeupEnd = false;
 
-        // Indicates that the Transaction contains a buffer that should be cached
-        bool mContainsBuffer = false;
+        // Indicates that the Transaction may contain buffers that should be cached. The reason this
+        // is only a guess is that buffers can be removed before cache is called. This is only a
+        // hint that at some point a buffer was added to this transaction before apply was called.
+        bool mMayContainBuffer = false;
 
         // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
         // to be presented. When it is not possible to present at exactly that time, it will be
@@ -468,10 +467,9 @@
         Transaction& merge(Transaction&& other);
         Transaction& show(const sp<SurfaceControl>& sc);
         Transaction& hide(const sp<SurfaceControl>& sc);
-        Transaction& setPosition(const sp<SurfaceControl>& sc,
-                float x, float y);
-        Transaction& setSize(const sp<SurfaceControl>& sc,
-                uint32_t w, uint32_t h);
+        Transaction& setPosition(const sp<SurfaceControl>& sc, float x, float y);
+        // b/243180033 remove once functions are not called from vendor code
+        Transaction& setSize(const sp<SurfaceControl>&, uint32_t, uint32_t) { return *this; }
         Transaction& setLayer(const sp<SurfaceControl>& sc,
                 int32_t z);
 
@@ -669,6 +667,9 @@
          * TODO (b/213644870): Remove all permissioned things from Transaction
          */
         void sanitize();
+
+        static sp<IBinder> getDefaultApplyToken();
+        static void setDefaultApplyToken(sp<IBinder> applyToken);
     };
 
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index e4a1350..1d4fc7f 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -78,6 +78,7 @@
     sp<IBinder> getHandle() const;
     sp<IBinder> getLayerStateHandle() const;
     int32_t getLayerId() const;
+    const std::string& getName() const;
 
     sp<IGraphicBufferProducer> getIGraphicBufferProducer();
 
@@ -94,8 +95,9 @@
     explicit SurfaceControl(const sp<SurfaceControl>& other);
 
     SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
-                   int32_t layerId, uint32_t width = 0, uint32_t height = 0, PixelFormat format = 0,
-                   uint32_t transformHint = 0, uint32_t flags = 0);
+                   int32_t layerId, const std::string& layerName, uint32_t width = 0,
+                   uint32_t height = 0, PixelFormat format = 0, uint32_t transformHint = 0,
+                   uint32_t flags = 0);
 
     sp<SurfaceControl> getParentingLayer();
 
@@ -121,6 +123,7 @@
     mutable sp<BLASTBufferQueue> mBbq;
     mutable sp<SurfaceControl> mBbqChild;
     int32_t mLayerId = 0;
+    std::string mName;
     uint32_t mTransformHint = 0;
     uint32_t mWidth = 0;
     uint32_t mHeight = 0;
diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h
index 0784fbc..bcf565a 100644
--- a/libs/gui/include/gui/SyncScreenCaptureListener.h
+++ b/libs/gui/include/gui/SyncScreenCaptureListener.h
@@ -34,7 +34,9 @@
     ScreenCaptureResults waitForResults() {
         std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future();
         const auto screenCaptureResults = resultsFuture.get();
-        screenCaptureResults.fence->waitForever("");
+        if (screenCaptureResults.fenceResult.ok()) {
+            screenCaptureResults.fenceResult.value()->waitForever("");
+        }
         return screenCaptureResults;
     }
 
@@ -42,4 +44,4 @@
     std::promise<ScreenCaptureResults> resultsPromise;
 };
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index 4c01683..441b833 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -30,6 +30,12 @@
 #define ATRACE_FORMAT_INSTANT(fmt, ...) \
     (CC_UNLIKELY(ATRACE_ENABLED()) && (TraceUtils::instantFormat(fmt, ##__VA_ARGS__), true))
 
+#define ALOGE_AND_TRACE(fmt, ...)                  \
+    do {                                           \
+        ALOGE(fmt, ##__VA_ARGS__);                 \
+        ATRACE_FORMAT_INSTANT(fmt, ##__VA_ARGS__); \
+    } while (false)
+
 namespace android {
 
 class TraceUtils {
diff --git a/libs/gui/include/gui/TransactionTracing.h b/libs/gui/include/gui/TransactionTracing.h
deleted file mode 100644
index 9efba47..0000000
--- a/libs/gui/include/gui/TransactionTracing.h
+++ /dev/null
@@ -1,41 +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.
- */
-
-#pragma once
-
-#include <android/gui/BnTransactionTraceListener.h>
-#include <utils/Mutex.h>
-
-namespace android {
-
-class TransactionTraceListener : public gui::BnTransactionTraceListener {
-    static std::mutex sMutex;
-    static sp<TransactionTraceListener> sInstance;
-
-    TransactionTraceListener();
-
-public:
-    static sp<TransactionTraceListener> getInstance();
-
-    binder::Status onToggled(bool enabled) override;
-
-    bool isTracingEnabled();
-
-private:
-    bool mTracingEnabled = false;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/fake/BufferData.h b/libs/gui/include/gui/fake/BufferData.h
new file mode 100644
index 0000000..725d11c
--- /dev/null
+++ b/libs/gui/include/gui/fake/BufferData.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/LayerState.h>
+
+namespace android::fake {
+
+// Class which exposes buffer properties from BufferData without holding on to an actual buffer
+class BufferData : public android::BufferData {
+public:
+    BufferData(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
+               uint64_t outUsage)
+          : mBufferId(bufferId),
+            mWidth(width),
+            mHeight(height),
+            mPixelFormat(pixelFormat),
+            mOutUsage(outUsage) {}
+    bool hasBuffer() const override { return mBufferId != 0; }
+    bool hasSameBuffer(const android::BufferData& other) const override {
+        return getId() == other.getId() && frameNumber == other.frameNumber;
+    }
+    uint32_t getWidth() const override { return mWidth; }
+    uint32_t getHeight() const override { return mHeight; }
+    uint64_t getId() const override { return mBufferId; }
+    PixelFormat getPixelFormat() const override { return mPixelFormat; }
+    uint64_t getUsage() const override { return mOutUsage; }
+
+private:
+    uint64_t mBufferId;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    int32_t mPixelFormat;
+    uint64_t mOutUsage;
+};
+
+} // namespace android::fake
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index 62d1496..08785b4 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -135,7 +135,8 @@
         void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
                                        nsecs_t latchTime) const {
             const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence,
-                         previousReleaseFence, transformHint, frameEvents] = surfaceControlStats;
+                         previousReleaseFence, transformHint, frameEvents, ignore] =
+                surfaceControlStats;
 
             ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence));
             ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0,
diff --git a/libs/gui/include/private/gui/ComposerServiceAIDL.h b/libs/gui/include/private/gui/ComposerServiceAIDL.h
index 2963583..6352a58 100644
--- a/libs/gui/include/private/gui/ComposerServiceAIDL.h
+++ b/libs/gui/include/private/gui/ComposerServiceAIDL.h
@@ -51,28 +51,6 @@
     // Get a connection to the Composer Service.  This will block until
     // a connection is established. Returns null if permission is denied.
     static sp<gui::ISurfaceComposer> getComposerService();
-
-    // the following two methods are moved from ISurfaceComposer.h
-    // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
-    std::optional<PhysicalDisplayId> getInternalDisplayId() const {
-        std::vector<int64_t> displayIds;
-        binder::Status status = mComposerService->getPhysicalDisplayIds(&displayIds);
-        return (!status.isOk() || displayIds.empty())
-                ? std::nullopt
-                : DisplayId::fromValue<PhysicalDisplayId>(
-                          static_cast<uint64_t>(displayIds.front()));
-    }
-
-    // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
-    sp<IBinder> getInternalDisplayToken() const {
-        const auto displayId = getInternalDisplayId();
-        if (!displayId) return nullptr;
-        sp<IBinder> display;
-        binder::Status status =
-                mComposerService->getPhysicalDisplayToken(static_cast<int64_t>(displayId->value),
-                                                          &display);
-        return status.isOk() ? display : nullptr;
-    }
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 3e563b2..cf2593d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -188,7 +188,10 @@
     void SetUp() {
         mComposer = ComposerService::getComposerService();
         mClient = new SurfaceComposerClient();
-        mDisplayToken = mClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        // display 0 is picked as this test is not much display depedent
+        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_NE(nullptr, mDisplayToken.get());
         Transaction t;
         t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
@@ -311,7 +314,7 @@
             return err;
         }
         captureResults = captureListener->waitForResults();
-        return captureResults.result;
+        return fenceStatus(captureResults.fenceResult);
     }
 
     void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index b647aab..0a2750a 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -32,7 +32,10 @@
     void SetUp() {
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(OK, mComposerClient->initCheck());
-        mDisplayToken = mComposerClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        // display 0 is picked for now, can extend to support all displays if needed
+        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_TRUE(mDisplayToken);
     }
 
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 2637f59..3344e0b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -360,8 +360,10 @@
     void SetUp() {
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        const auto display = mComposerClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        // display 0 is picked for now, can extend to support all displays if needed
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_NE(display, nullptr);
 
         ui::DisplayMode mode;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 71f2ad4..72d76ee 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -218,7 +218,7 @@
             return err;
         }
         captureResults = captureListener->waitForResults();
-        return captureResults.result;
+        return fenceStatus(captureResults.fenceResult);
     }
 
     sp<Surface> mSurface;
@@ -263,7 +263,10 @@
     sp<ANativeWindow> anw(mSurface);
 
     // Verify the screenshot works with no protected buffers.
-    const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    // display 0 is picked for now, can extend to support all displays if needed
+    const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ASSERT_FALSE(display == nullptr);
 
     DisplayCaptureArgs captureArgs;
@@ -848,10 +851,6 @@
         return binder::Status::ok();
     }
 
-    binder::Status enableVSyncInjections(bool /*enable*/) override { return binder::Status::ok(); }
-
-    binder::Status injectVSync(int64_t /*when*/) override { return binder::Status::ok(); }
-
     binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* /*outLayers*/) override {
         return binder::Status::ok();
     }
@@ -974,11 +973,6 @@
         return binder::Status::ok();
     }
 
-    binder::Status addTransactionTraceListener(
-            const sp<gui::ITransactionTraceListener>& /*listener*/) override {
-        return binder::Status::ok();
-    }
-
     binder::Status getGpuContextPriority(int32_t* /*outPriority*/) override {
         return binder::Status::ok();
     }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 5030d60..34ef7b4 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -26,6 +26,7 @@
 filegroup {
     name: "inputconstants_aidl",
     srcs: [
+        "android/hardware/input/InputDeviceCountryCode.aidl",
         "android/os/IInputConstants.aidl",
         "android/os/InputEventInjectionResult.aidl",
         "android/os/InputEventInjectionSync.aidl",
@@ -88,7 +89,6 @@
             shared_libs: [
                 "libutils",
                 "libbinder",
-                "libui",
             ],
 
             static_libs: [
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 155cb04..579b28e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -87,6 +87,8 @@
             return "AMBIGUOUS_GESTURE";
         case MotionClassification::DEEP_PRESS:
             return "DEEP_PRESS";
+        case MotionClassification::TWO_FINGER_SWIPE:
+            return "TWO_FINGER_SWIPE";
     }
 }
 
@@ -949,6 +951,8 @@
         out << ", actionButton=" << std::to_string(event.getActionButton());
     }
     const size_t pointerCount = event.getPointerCount();
+    LOG_ALWAYS_FATAL_IF(pointerCount > MAX_POINTERS, "Too many pointers : pointerCount = %zu",
+                        pointerCount);
     for (size_t i = 0; i < pointerCount; i++) {
         out << ", id[" << i << "]=" << event.getPointerId(i);
         float x = event.getX(i);
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index a908969..4751a7d 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -26,6 +26,7 @@
 #include <input/InputEventLabels.h>
 
 using android::base::StringPrintf;
+using android::hardware::input::InputDeviceCountryCode;
 
 namespace android {
 
@@ -177,9 +178,11 @@
         mAlias(other.mAlias),
         mIsExternal(other.mIsExternal),
         mHasMic(other.mHasMic),
+        mCountryCode(other.mCountryCode),
         mSources(other.mSources),
         mKeyboardType(other.mKeyboardType),
         mKeyCharacterMap(other.mKeyCharacterMap),
+        mSupportsUsi(other.mSupportsUsi),
         mHasVibrator(other.mHasVibrator),
         mHasBattery(other.mHasBattery),
         mHasButtonUnderPad(other.mHasButtonUnderPad),
@@ -192,8 +195,8 @@
 }
 
 void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber,
-        const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal,
-        bool hasMic) {
+                                 const InputDeviceIdentifier& identifier, const std::string& alias,
+                                 bool isExternal, bool hasMic, InputDeviceCountryCode countryCode) {
     mId = id;
     mGeneration = generation;
     mControllerNumber = controllerNumber;
@@ -201,12 +204,14 @@
     mAlias = alias;
     mIsExternal = isExternal;
     mHasMic = hasMic;
+    mCountryCode = countryCode;
     mSources = 0;
     mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
     mHasVibrator = false;
     mHasBattery = false;
     mHasButtonUnderPad = false;
     mHasSensor = false;
+    mSupportsUsi = false;
     mMotionRanges.clear();
     mSensors.clear();
     mLights.clear();
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index c0aa2e2..163a2fe 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -23,6 +23,8 @@
 
 namespace android {
 
+// clang-format off
+
 // NOTE: If you add a new keycode here you must also add it to several other files.
 //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
 #define KEYCODES_SEQUENCE \
@@ -314,7 +316,30 @@
     DEFINE_KEYCODE(REFRESH), \
     DEFINE_KEYCODE(THUMBS_UP), \
     DEFINE_KEYCODE(THUMBS_DOWN), \
-    DEFINE_KEYCODE(PROFILE_SWITCH)
+    DEFINE_KEYCODE(PROFILE_SWITCH), \
+    DEFINE_KEYCODE(VIDEO_APP_1), \
+    DEFINE_KEYCODE(VIDEO_APP_2), \
+    DEFINE_KEYCODE(VIDEO_APP_3), \
+    DEFINE_KEYCODE(VIDEO_APP_4), \
+    DEFINE_KEYCODE(VIDEO_APP_5), \
+    DEFINE_KEYCODE(VIDEO_APP_6), \
+    DEFINE_KEYCODE(VIDEO_APP_7), \
+    DEFINE_KEYCODE(VIDEO_APP_8), \
+    DEFINE_KEYCODE(FEATURED_APP_1), \
+    DEFINE_KEYCODE(FEATURED_APP_2), \
+    DEFINE_KEYCODE(FEATURED_APP_3), \
+    DEFINE_KEYCODE(FEATURED_APP_4), \
+    DEFINE_KEYCODE(DEMO_APP_1), \
+    DEFINE_KEYCODE(DEMO_APP_2), \
+    DEFINE_KEYCODE(DEMO_APP_3), \
+    DEFINE_KEYCODE(DEMO_APP_4), \
+    DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_DOWN), \
+    DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_UP), \
+    DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_TOGGLE), \
+    DEFINE_KEYCODE(STYLUS_BUTTON_PRIMARY), \
+    DEFINE_KEYCODE(STYLUS_BUTTON_SECONDARY), \
+    DEFINE_KEYCODE(STYLUS_BUTTON_TERTIARY), \
+    DEFINE_KEYCODE(STYLUS_BUTTON_TAIL)
 
 // NOTE: If you add a new axis here you must also add it to several other files.
 //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
@@ -368,7 +393,6 @@
     DEFINE_AXIS(GENERIC_15), \
     DEFINE_AXIS(GENERIC_16)
 
-
 // NOTE: If you add new LEDs here, you must also add them to Input.h
 #define LEDS_SEQUENCE \
     DEFINE_LED(NUM_LOCK), \
@@ -393,6 +417,8 @@
     DEFINE_FLAG(GESTURE), \
     DEFINE_FLAG(WAKE)
 
+// clang-format on
+
 // --- InputEventLookup ---
 const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE};
 
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 59cc7d1..7371033 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -25,8 +25,10 @@
 #include <utils/Errors.h>
 #include <utils/Timers.h>
 #include <utils/Tokenizer.h>
+#if defined(__ANDROID__)
 #include <vintf/RuntimeInfo.h>
 #include <vintf/VintfObject.h>
+#endif
 
 #include <cstdlib>
 #include <string_view>
@@ -79,6 +81,7 @@
          sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()};
 
 bool kernelConfigsArePresent(const std::set<std::string>& configs) {
+#if defined(__ANDROID__)
     std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo =
             android::vintf::VintfObject::GetInstance()->getRuntimeInfo(
                     vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
@@ -99,6 +102,10 @@
         }
     }
     return true;
+#else
+    (void)configs; // Suppress 'unused variable' warning
+    return true;
+#endif
 }
 
 } // namespace
@@ -185,7 +192,8 @@
 }
 
 // Return pair of sensor type and sensor data index, for the input device abs code
-base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) {
+base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(
+        int32_t absCode) const {
     auto it = mSensorsByAbsCode.find(absCode);
     if (it == mSensorsByAbsCode.end()) {
         ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, ~ Failed.", absCode);
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 662e568..16ffa10 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -75,7 +75,7 @@
     }
 
     char* end;
-    int value = strtol(stringValue.c_str(), &end, 10);
+    int32_t value = static_cast<int32_t>(strtol(stringValue.c_str(), &end, 10));
     if (*end != '\0') {
         ALOGW("Property key '%s' has invalid value '%s'.  Expected an integer.", key.c_str(),
               stringValue.c_str());
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index 6e991e9..e2bfb50 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -44,8 +44,8 @@
 
 void VelocityControl::reset() {
     mLastMovementTime = LLONG_MIN;
-    mRawPosition.x = 0;
-    mRawPosition.y = 0;
+    mRawPositionX = 0;
+    mRawPositionY = 0;
     mVelocityTracker.clear();
 }
 
@@ -61,17 +61,20 @@
 
         mLastMovementTime = eventTime;
         if (deltaX) {
-            mRawPosition.x += *deltaX;
+            mRawPositionX += *deltaX;
         }
         if (deltaY) {
-            mRawPosition.y += *deltaY;
+            mRawPositionY += *deltaY;
         }
-        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition});
+        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)),
+                                     {{AMOTION_EVENT_AXIS_X, {mRawPositionX}},
+                                      {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}});
 
-        float vx, vy;
+        std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0);
+        std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
         float scale = mParameters.scale;
-        if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
-            float speed = hypotf(vx, vy) * scale;
+        if (vx && vy) {
+            float speed = hypotf(*vx, *vy) * scale;
             if (speed >= mParameters.highThreshold) {
                 // Apply full acceleration above the high speed threshold.
                 scale *= mParameters.acceleration;
@@ -85,10 +88,9 @@
 
             if (DEBUG_ACCELERATION) {
                 ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
-                        "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
-                        mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
-                        mParameters.acceleration,
-                        vx, vy, speed, scale / mParameters.scale);
+                      "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
+                      mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
+                      mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale);
             }
 
         } else {
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 76aaf61..4a4f734 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -55,6 +55,22 @@
 // Nanoseconds per milliseconds.
 static const nsecs_t NANOS_PER_MS = 1000000;
 
+// All axes supported for velocity tracking, mapped to their default strategies.
+// Although other strategies are available for testing and comparison purposes,
+// the default strategy is the one that applications will actually use.  Be very careful
+// when adjusting the default strategy because it can dramatically affect
+// (often in a bad way) the user experience.
+static const std::map<int32_t, VelocityTracker::Strategy> DEFAULT_STRATEGY_BY_AXIS =
+        {{AMOTION_EVENT_AXIS_X, VelocityTracker::Strategy::LSQ2},
+         {AMOTION_EVENT_AXIS_Y, VelocityTracker::Strategy::LSQ2},
+         {AMOTION_EVENT_AXIS_SCROLL, VelocityTracker::Strategy::IMPULSE}};
+
+// Axes specifying location on a 2D plane (i.e. X and Y).
+static const std::set<int32_t> PLANAR_AXES = {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y};
+
+// Axes whose motion values are differential values (i.e. deltas).
+static const std::set<int32_t> DIFFERENTIAL_AXES = {AMOTION_EVENT_AXIS_SCROLL};
+
 // Threshold for determining that a pointer has stopped moving.
 // Some input devices do not send ACTION_MOVE events in the case where a pointer has
 // stopped.  We need to detect this case so that we can accurately predict the
@@ -126,36 +142,36 @@
 // --- VelocityTracker ---
 
 VelocityTracker::VelocityTracker(const Strategy strategy)
-      : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
-    // Configure the strategy.
-    if (!configureStrategy(strategy)) {
-        ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
-        if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
-            LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
-                             "'!",
-                             strategy);
-        }
-    }
-}
+      : mLastEventTime(0),
+        mCurrentPointerIdBits(0),
+        mActivePointerId(-1),
+        mOverrideStrategy(strategy) {}
 
 VelocityTracker::~VelocityTracker() {
 }
 
-bool VelocityTracker::configureStrategy(Strategy strategy) {
-    if (strategy == VelocityTracker::Strategy::DEFAULT) {
-        mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+void VelocityTracker::configureStrategy(int32_t axis) {
+    const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end();
+
+    std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
+    if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) {
+        createdStrategy = createStrategy(mOverrideStrategy, isDifferentialAxis /* deltaValues */);
     } else {
-        mStrategy = createStrategy(strategy);
+        createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
+                                         isDifferentialAxis /* deltaValues */);
     }
-    return mStrategy != nullptr;
+
+    LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
+                        "Could not create velocity tracker strategy for axis '%" PRId32 "'!", axis);
+    mConfiguredStrategies[axis] = std::move(createdStrategy);
 }
 
 std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
-        VelocityTracker::Strategy strategy) {
+        VelocityTracker::Strategy strategy, bool deltaValues) {
     switch (strategy) {
         case VelocityTracker::Strategy::IMPULSE:
             ALOGI_IF(DEBUG_STRATEGY, "Initializing impulse strategy");
-            return std::make_unique<ImpulseVelocityTrackerStrategy>();
+            return std::make_unique<ImpulseVelocityTrackerStrategy>(deltaValues);
 
         case VelocityTracker::Strategy::LSQ1:
             return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
@@ -201,8 +217,7 @@
 void VelocityTracker::clear() {
     mCurrentPointerIdBits.clear();
     mActivePointerId = -1;
-
-    mStrategy->clear();
+    mConfiguredStrategies.clear();
 }
 
 void VelocityTracker::clearPointers(BitSet32 idBits) {
@@ -213,14 +228,13 @@
         mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
     }
 
-    mStrategy->clearPointers(idBits);
+    for (const auto& [_, strategy] : mConfiguredStrategies) {
+        strategy->clearPointers(idBits);
+    }
 }
 
 void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                  const std::vector<VelocityTracker::Position>& positions) {
-    LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(),
-                        "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
-                        idBits.count(), positions.size());
+                                  const std::map<int32_t /*axis*/, std::vector<float>>& positions) {
     while (idBits.count() > MAX_POINTERS) {
         idBits.clearLastMarkedBit();
     }
@@ -232,7 +246,7 @@
 
         // We have not received any movements for too long.  Assume that all pointers
         // have stopped.
-        mStrategy->clear();
+        mConfiguredStrategies.clear();
     }
     mLastEventTime = eventTime;
 
@@ -241,29 +255,40 @@
         mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit();
     }
 
-    mStrategy->addMovement(eventTime, idBits, positions);
+    for (const auto& [axis, positionValues] : positions) {
+        LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(),
+                            "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
+                            idBits.count(), positionValues.size());
+        if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) {
+            configureStrategy(axis);
+        }
+        mConfiguredStrategies[axis]->addMovement(eventTime, idBits, positionValues);
+    }
 
     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);
+        for (const auto& positionsEntry : positions) {
+            for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
+                uint32_t id = iterBits.firstMarkedBit();
+                uint32_t index = idBits.getIndexOfBit(id);
+                iterBits.clearBit(id);
+                Estimator estimator;
+                getEstimator(positionsEntry.first, id, &estimator);
+                ALOGD("  %d: axis=%d, position=%0.3f, "
+                      "estimator (degree=%d, coeff=%s, confidence=%f)",
+                      id, positionsEntry.first, positionsEntry.second[index], int(estimator.degree),
+                      vectorToString(estimator.coeff, estimator.degree + 1).c_str(),
+                      estimator.confidence);
+            }
         }
     }
 }
 
 void VelocityTracker::addMovement(const MotionEvent* event) {
+    // Stores data about which axes to process based on the incoming motion event.
+    std::set<int32_t> axesToProcess;
     int32_t actionMasked = event->getActionMasked();
 
     switch (actionMasked) {
@@ -271,6 +296,7 @@
     case AMOTION_EVENT_ACTION_HOVER_ENTER:
         // Clear all pointers on down before adding the new movement.
         clear();
+        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
         break;
     case AMOTION_EVENT_ACTION_POINTER_DOWN: {
         // Start a new movement trace for a pointer that just went down.
@@ -279,10 +305,12 @@
         BitSet32 downIdBits;
         downIdBits.markBit(event->getPointerId(event->getActionIndex()));
         clearPointers(downIdBits);
+        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
         break;
     }
     case AMOTION_EVENT_ACTION_MOVE:
     case AMOTION_EVENT_ACTION_HOVER_MOVE:
+        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
         break;
     case AMOTION_EVENT_ACTION_POINTER_UP:
     case AMOTION_EVENT_ACTION_UP: {
@@ -293,7 +321,9 @@
                      toString(delaySinceLastEvent).c_str());
             // We have not received any movements for too long.  Assume that all pointers
             // have stopped.
-            mStrategy->clear();
+            for (int32_t axis : PLANAR_AXES) {
+                mConfiguredStrategies.erase(axis);
+            }
         }
         // These actions because they do not convey any new information about
         // pointer movement.  We also want to preserve the last known velocity of the pointers.
@@ -305,6 +335,9 @@
         // before adding the movement.
         return;
     }
+    case AMOTION_EVENT_ACTION_SCROLL:
+        axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL);
+        break;
     default:
         // Ignore all other actions.
         return;
@@ -325,62 +358,74 @@
         pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
     }
 
-    std::vector<Position> positions;
-    positions.resize(pointerCount);
+    std::map<int32_t, std::vector<float>> positions;
+    for (int32_t axis : axesToProcess) {
+        positions[axis].resize(pointerCount);
+    }
 
     size_t historySize = event->getHistorySize();
     for (size_t h = 0; h <= historySize; h++) {
         nsecs_t eventTime = event->getHistoricalEventTime(h);
-        for (size_t i = 0; i < pointerCount; i++) {
-            uint32_t index = pointerIndex[i];
-            positions[index].x = event->getHistoricalX(i, h);
-            positions[index].y = event->getHistoricalY(i, h);
+        for (int32_t axis : axesToProcess) {
+            for (size_t i = 0; i < pointerCount; i++) {
+                positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h);
+            }
         }
         addMovement(eventTime, idBits, positions);
     }
 }
 
-bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
+std::optional<float> VelocityTracker::getVelocity(int32_t axis, uint32_t id) const {
     Estimator estimator;
-    if (getEstimator(id, &estimator) && estimator.degree >= 1) {
-        *outVx = estimator.xCoeff[1];
-        *outVy = estimator.yCoeff[1];
-        return true;
+    bool validVelocity = getEstimator(axis, id, &estimator) && estimator.degree >= 1;
+    if (validVelocity) {
+        return estimator.coeff[1];
     }
-    *outVx = 0;
-    *outVy = 0;
-    return false;
+    return {};
 }
 
-bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const {
-    return mStrategy->getEstimator(id, outEstimator);
+VelocityTracker::ComputedVelocity VelocityTracker::getComputedVelocity(int32_t units,
+                                                                       float maxVelocity) {
+    ComputedVelocity computedVelocity;
+    for (const auto& [axis, _] : mConfiguredStrategies) {
+        BitSet32 copyIdBits = BitSet32(mCurrentPointerIdBits);
+        while (!copyIdBits.isEmpty()) {
+            uint32_t id = copyIdBits.clearFirstMarkedBit();
+            std::optional<float> velocity = getVelocity(axis, id);
+            if (velocity) {
+                float adjustedVelocity =
+                        std::clamp(*velocity * units / 1000, -maxVelocity, maxVelocity);
+                computedVelocity.addVelocity(axis, id, adjustedVelocity);
+            }
+        }
+    }
+    return computedVelocity;
 }
 
+bool VelocityTracker::getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const {
+    const auto& it = mConfiguredStrategies.find(axis);
+    if (it == mConfiguredStrategies.end()) {
+        return false;
+    }
+    return it->second->getEstimator(id, outEstimator);
+}
 
 // --- LeastSquaresVelocityTrackerStrategy ---
 
-LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(
-        uint32_t degree, Weighting weighting) :
-        mDegree(degree), mWeighting(weighting) {
-    clear();
-}
+LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree,
+                                                                         Weighting weighting)
+      : mDegree(degree), mWeighting(weighting), mIndex(0) {}
 
 LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {
 }
 
-void LeastSquaresVelocityTrackerStrategy::clear() {
-    mIndex = 0;
-    mMovements[0].idBits.clear();
-}
-
 void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
     BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LeastSquaresVelocityTrackerStrategy::addMovement(
-        nsecs_t eventTime, BitSet32 idBits,
-        const std::vector<VelocityTracker::Position>& positions) {
+void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                                      const std::vector<float>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -627,8 +672,7 @@
     outEstimator->clear();
 
     // Iterate over movement samples in reverse time order and collect samples.
-    std::vector<float> x;
-    std::vector<float> y;
+    std::vector<float> positions;
     std::vector<float> w;
     std::vector<float> time;
 
@@ -645,15 +689,13 @@
             break;
         }
 
-        const VelocityTracker::Position& position = movement.getPosition(id);
-        x.push_back(position.x);
-        y.push_back(position.y);
+        positions.push_back(movement.getPosition(id));
         w.push_back(chooseWeight(index));
         time.push_back(-age * 0.000000001f);
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
-    } while (x.size() < HISTORY_SIZE);
+    } while (positions.size() < HISTORY_SIZE);
 
-    const size_t m = x.size();
+    const size_t m = positions.size();
     if (m == 0) {
         return false; // no data
     }
@@ -666,39 +708,36 @@
 
     if (degree == 2 && mWeighting == WEIGHTING_NONE) {
         // Optimize unweighted, quadratic polynomial fit
-        std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x);
-        std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y);
-        if (xCoeff && yCoeff) {
+        std::optional<std::array<float, 3>> coeff =
+                solveUnweightedLeastSquaresDeg2(time, positions);
+        if (coeff) {
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = 2;
             outEstimator->confidence = 1;
             for (size_t i = 0; i <= outEstimator->degree; i++) {
-                outEstimator->xCoeff[i] = (*xCoeff)[i];
-                outEstimator->yCoeff[i] = (*yCoeff)[i];
+                outEstimator->coeff[i] = (*coeff)[i];
             }
             return true;
         }
     } else if (degree >= 1) {
         // General case for an Nth degree polynomial fit
-        float xdet, ydet;
+        float det;
         uint32_t n = degree + 1;
-        if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) &&
-            solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) {
+        if (solveLeastSquares(time, positions, w, n, outEstimator->coeff, &det)) {
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = degree;
-            outEstimator->confidence = xdet * ydet;
+            outEstimator->confidence = det;
 
-            ALOGD_IF(DEBUG_STRATEGY, "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);
+            ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f",
+                     int(outEstimator->degree), vectorToString(outEstimator->coeff, n).c_str(),
+                     outEstimator->confidence);
 
             return true;
         }
     }
 
     // No velocity data available for this pointer, but we do have its current position.
-    outEstimator->xCoeff[0] = x[0];
-    outEstimator->yCoeff[0] = y[0];
+    outEstimator->coeff[0] = positions[0];
     outEstimator->time = newestMovement.eventTime;
     outEstimator->degree = 0;
     outEstimator->confidence = 1;
@@ -782,26 +821,21 @@
 IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {
 }
 
-void IntegratingVelocityTrackerStrategy::clear() {
-    mPointerIdBits.clear();
-}
-
 void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
     mPointerIdBits.value &= ~idBits.value;
 }
 
-void IntegratingVelocityTrackerStrategy::addMovement(
-        nsecs_t eventTime, BitSet32 idBits,
-        const std::vector<VelocityTracker::Position>& positions) {
+void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                                     const std::vector<float>& positions) {
     uint32_t index = 0;
     for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
         uint32_t id = iterIdBits.clearFirstMarkedBit();
         State& state = mPointerState[id];
-        const VelocityTracker::Position& position = positions[index++];
+        const float position = positions[index++];
         if (mPointerIdBits.hasBit(id)) {
-            updateState(state, eventTime, position.x, position.y);
+            updateState(state, eventTime, position);
         } else {
-            initState(state, eventTime, position.x, position.y);
+            initState(state, eventTime, position);
         }
     }
 
@@ -821,21 +855,18 @@
     return false;
 }
 
-void IntegratingVelocityTrackerStrategy::initState(State& state,
-        nsecs_t eventTime, float xpos, float ypos) const {
+void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime,
+                                                   float pos) const {
     state.updateTime = eventTime;
     state.degree = 0;
 
-    state.xpos = xpos;
-    state.xvel = 0;
-    state.xaccel = 0;
-    state.ypos = ypos;
-    state.yvel = 0;
-    state.yaccel = 0;
+    state.pos = pos;
+    state.accel = 0;
+    state.vel = 0;
 }
 
-void IntegratingVelocityTrackerStrategy::updateState(State& state,
-        nsecs_t eventTime, float xpos, float ypos) const {
+void IntegratingVelocityTrackerStrategy::updateState(State& state, nsecs_t eventTime,
+                                                     float pos) const {
     const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS;
     const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds
 
@@ -846,34 +877,26 @@
     float dt = (eventTime - state.updateTime) * 0.000000001f;
     state.updateTime = eventTime;
 
-    float xvel = (xpos - state.xpos) / dt;
-    float yvel = (ypos - state.ypos) / dt;
+    float vel = (pos - state.pos) / dt;
     if (state.degree == 0) {
-        state.xvel = xvel;
-        state.yvel = yvel;
+        state.vel = vel;
         state.degree = 1;
     } else {
         float alpha = dt / (FILTER_TIME_CONSTANT + dt);
         if (mDegree == 1) {
-            state.xvel += (xvel - state.xvel) * alpha;
-            state.yvel += (yvel - state.yvel) * alpha;
+            state.vel += (vel - state.vel) * alpha;
         } else {
-            float xaccel = (xvel - state.xvel) / dt;
-            float yaccel = (yvel - state.yvel) / dt;
+            float accel = (vel - state.vel) / dt;
             if (state.degree == 1) {
-                state.xaccel = xaccel;
-                state.yaccel = yaccel;
+                state.accel = accel;
                 state.degree = 2;
             } else {
-                state.xaccel += (xaccel - state.xaccel) * alpha;
-                state.yaccel += (yaccel - state.yaccel) * alpha;
+                state.accel += (accel - state.accel) * alpha;
             }
-            state.xvel += (state.xaccel * dt) * alpha;
-            state.yvel += (state.yaccel * dt) * alpha;
+            state.vel += (state.accel * dt) * alpha;
         }
     }
-    state.xpos = xpos;
-    state.ypos = ypos;
+    state.pos = pos;
 }
 
 void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
@@ -881,37 +904,26 @@
     outEstimator->time = state.updateTime;
     outEstimator->confidence = 1.0f;
     outEstimator->degree = state.degree;
-    outEstimator->xCoeff[0] = state.xpos;
-    outEstimator->xCoeff[1] = state.xvel;
-    outEstimator->xCoeff[2] = state.xaccel / 2;
-    outEstimator->yCoeff[0] = state.ypos;
-    outEstimator->yCoeff[1] = state.yvel;
-    outEstimator->yCoeff[2] = state.yaccel / 2;
+    outEstimator->coeff[0] = state.pos;
+    outEstimator->coeff[1] = state.vel;
+    outEstimator->coeff[2] = state.accel / 2;
 }
 
 
 // --- LegacyVelocityTrackerStrategy ---
 
-LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {
-    clear();
-}
+LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() : mIndex(0) {}
 
 LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() {
 }
 
-void LegacyVelocityTrackerStrategy::clear() {
-    mIndex = 0;
-    mMovements[0].idBits.clear();
-}
-
 void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
     BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LegacyVelocityTrackerStrategy::addMovement(
-        nsecs_t eventTime, BitSet32 idBits,
-        const std::vector<VelocityTracker::Position>& positions) {
+void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                                const std::vector<float>& positions) {
     if (++mIndex == HISTORY_SIZE) {
         mIndex = 0;
     }
@@ -959,12 +971,11 @@
     // overestimate the velocity at that time point.  Most samples might be measured
     // 16ms apart but some consecutive samples could be only 0.5sm apart because
     // the hardware or driver reports them irregularly or in bursts.
-    float accumVx = 0;
-    float accumVy = 0;
+    float accumV = 0;
     uint32_t index = oldestIndex;
     uint32_t samplesUsed = 0;
     const Movement& oldestMovement = mMovements[oldestIndex];
-    const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id);
+    float oldestPosition = oldestMovement.getPosition(id);
     nsecs_t lastDuration = 0;
 
     while (numTouches-- > 1) {
@@ -978,26 +989,22 @@
         // the velocity.  Consequently, we impose a minimum duration constraint on the
         // samples that we include in the calculation.
         if (duration >= MIN_DURATION) {
-            const VelocityTracker::Position& position = movement.getPosition(id);
+            float position = movement.getPosition(id);
             float scale = 1000000000.0f / duration; // one over time delta in seconds
-            float vx = (position.x - oldestPosition.x) * scale;
-            float vy = (position.y - oldestPosition.y) * scale;
-            accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
-            accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
+            float v = (position - oldestPosition) * scale;
+            accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration);
             lastDuration = duration;
             samplesUsed += 1;
         }
     }
 
     // Report velocity.
-    const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id);
+    float newestPosition = newestMovement.getPosition(id);
     outEstimator->time = newestMovement.eventTime;
     outEstimator->confidence = 1;
-    outEstimator->xCoeff[0] = newestPosition.x;
-    outEstimator->yCoeff[0] = newestPosition.y;
+    outEstimator->coeff[0] = newestPosition;
     if (samplesUsed) {
-        outEstimator->xCoeff[1] = accumVx;
-        outEstimator->yCoeff[1] = accumVy;
+        outEstimator->coeff[1] = accumV;
         outEstimator->degree = 1;
     } else {
         outEstimator->degree = 0;
@@ -1007,26 +1014,19 @@
 
 // --- ImpulseVelocityTrackerStrategy ---
 
-ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy() {
-    clear();
-}
+ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues)
+      : mDeltaValues(deltaValues), mIndex(0) {}
 
 ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() {
 }
 
-void ImpulseVelocityTrackerStrategy::clear() {
-    mIndex = 0;
-    mMovements[0].idBits.clear();
-}
-
 void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
     BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void ImpulseVelocityTrackerStrategy::addMovement(
-        nsecs_t eventTime, BitSet32 idBits,
-        const std::vector<VelocityTracker::Position>& positions) {
+void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                                 const std::vector<float>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -1123,7 +1123,8 @@
     return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2;
 }
 
-static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count) {
+static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count,
+                                      bool deltaValues) {
     // The input should be in reversed time order (most recent sample at index i=0)
     // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function
     static constexpr float SECONDS_PER_NANO = 1E-9;
@@ -1134,12 +1135,26 @@
     if (t[1] > t[0]) { // Algorithm will still work, but not perfectly
         ALOGE("Samples provided to calculateImpulseVelocity in the wrong order");
     }
+
+    // If the data values are delta values, we do not have to calculate deltas here.
+    // We can use the delta values directly, along with the calculated time deltas.
+    // Since the data value input is in reversed time order:
+    //      [a] for non-delta inputs, instantenous velocity = (x[i] - x[i-1])/(t[i] - t[i-1])
+    //      [b] for delta inputs, instantenous velocity = -x[i-1]/(t[i] - t[i - 1])
+    // e.g., let the non-delta values are: V = [2, 3, 7], the equivalent deltas are D = [2, 1, 4].
+    // Since the input is in reversed time order, the input values for this function would be
+    // V'=[7, 3, 2] and D'=[4, 1, 2] for the non-delta and delta values, respectively.
+    //
+    // The equivalent of {(V'[2] - V'[1]) = 2 - 3 = -1} would be {-D'[1] = -1}
+    // Similarly, the equivalent of {(V'[1] - V'[0]) = 3 - 7 = -4} would be {-D'[0] = -4}
+
     if (count == 2) { // if 2 points, basic linear calculation
         if (t[1] == t[0]) {
             ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]);
             return 0;
         }
-        return (x[1] - x[0]) / (SECONDS_PER_NANO * (t[1] - t[0]));
+        const float deltaX = deltaValues ? -x[0] : x[1] - x[0];
+        return deltaX / (SECONDS_PER_NANO * (t[1] - t[0]));
     }
     // Guaranteed to have at least 3 points here
     float work = 0;
@@ -1149,7 +1164,8 @@
             continue;
         }
         float vprev = kineticEnergyToVelocity(work); // v[i-1]
-        float vcurr = (x[i] - x[i-1]) / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i]
+        const float deltaX = deltaValues ? -x[i-1] : x[i] - x[i-1];
+        float vcurr = deltaX / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i]
         work += (vcurr - vprev) * fabsf(vcurr);
         if (i == count - 1) {
             work *= 0.5; // initial condition, case 2) above
@@ -1163,8 +1179,7 @@
     outEstimator->clear();
 
     // Iterate over movement samples in reverse time order and collect samples.
-    float x[HISTORY_SIZE];
-    float y[HISTORY_SIZE];
+    float positions[HISTORY_SIZE];
     nsecs_t time[HISTORY_SIZE];
     size_t m = 0; // number of points that will be used for fitting
     size_t index = mIndex;
@@ -1180,9 +1195,7 @@
             break;
         }
 
-        const VelocityTracker::Position& position = movement.getPosition(id);
-        x[m] = position.x;
-        y[m] = position.y;
+        positions[m] = movement.getPosition(id);
         time[m] = movement.eventTime;
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
     } while (++m < HISTORY_SIZE);
@@ -1190,33 +1203,30 @@
     if (m == 0) {
         return false; // no data
     }
-    outEstimator->xCoeff[0] = 0;
-    outEstimator->yCoeff[0] = 0;
-    outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m);
-    outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m);
-    outEstimator->xCoeff[2] = 0;
-    outEstimator->yCoeff[2] = 0;
+    outEstimator->coeff[0] = 0;
+    outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues);
+    outEstimator->coeff[2] = 0;
+
     outEstimator->time = newestMovement.eventTime;
     outEstimator->degree = 2; // similar results to 2nd degree fit
     outEstimator->confidence = 1;
 
-    ALOGD_IF(DEBUG_STRATEGY, "velocity: (%.1f, %.1f)", outEstimator->xCoeff[1],
-             outEstimator->yCoeff[1]);
+    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", outEstimator->coeff[1]);
 
     if (DEBUG_IMPULSE) {
         // TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
-        // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons
+        // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons.
+        // X axis chosen arbitrarily for velocity comparisons.
         VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2);
         BitSet32 idBits;
         const uint32_t pointerId = 0;
         idBits.markBit(pointerId);
         for (ssize_t i = m - 1; i >= 0; i--) {
-            lsq2.addMovement(time[i], idBits, {{x[i], y[i]}});
+            lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}});
         }
-        float outVx = 0, outVy = 0;
-        const bool computed = lsq2.getVelocity(pointerId, &outVx, &outVy);
-        if (computed) {
-            ALOGD("lsq2 velocity: (%.1f, %.1f)", outVx, outVy);
+        std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId);
+        if (v) {
+            ALOGD("lsq2 velocity: %.1f", *v);
         } else {
             ALOGD("lsq2 velocity: could not compute velocity");
         }
diff --git a/libs/input/android/hardware/input/InputDeviceCountryCode.aidl b/libs/input/android/hardware/input/InputDeviceCountryCode.aidl
new file mode 100644
index 0000000..6bb1a60
--- /dev/null
+++ b/libs/input/android/hardware/input/InputDeviceCountryCode.aidl
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+/**
+ * Constant for HID country code declared by a HID device. These constants are declared as AIDL to
+ * be used by java and native input code.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum InputDeviceCountryCode {
+    /**
+     * Used as default value where country code is not set in the device HID descriptor
+     */
+    INVALID = -1,
+
+    /**
+     * Used as default value when country code is not supported by the HID device. The HID
+     * descriptor sets "00" as the country code in this case.
+     */
+    NOT_SUPPORTED = 0,
+
+    /**
+     * Arabic
+     */
+    ARABIC = 1,
+
+    /**
+     * Belgian
+     */
+    BELGIAN = 2,
+
+    /**
+     * Canadian (Bilingual)
+     */
+    CANADIAN_BILINGUAL = 3,
+
+    /**
+     * Canadian (French)
+     */
+    CANADIAN_FRENCH = 4,
+
+    /**
+     * Czech Republic
+     */
+    CZECH_REPUBLIC = 5,
+
+    /**
+     * Danish
+     */
+    DANISH = 6,
+
+    /**
+     * Finnish
+     */
+    FINNISH = 7,
+
+    /**
+     * French
+     */
+    FRENCH = 8,
+
+    /**
+     * German
+     */
+    GERMAN = 9,
+
+    /**
+     * Greek
+     */
+    GREEK = 10,
+
+    /**
+     * Hebrew
+     */
+    HEBREW = 11,
+
+    /**
+     * Hungary
+     */
+    HUNGARY = 12,
+
+    /**
+     * International (ISO)
+     */
+    INTERNATIONAL = 13,
+
+    /**
+     * Italian
+     */
+    ITALIAN = 14,
+
+    /**
+     * Japan (Katakana)
+     */
+    JAPAN = 15,
+
+    /**
+     * Korean
+     */
+    KOREAN = 16,
+
+    /**
+     * Latin American
+     */
+    LATIN_AMERICAN = 17,
+
+    /**
+     * Netherlands (Dutch)
+     */
+    DUTCH = 18,
+
+    /**
+     * Norwegian
+     */
+    NORWEGIAN = 19,
+
+    /**
+     * Persian
+     */
+    PERSIAN = 20,
+
+    /**
+     * Poland
+     */
+    POLAND = 21,
+
+    /**
+     * Portuguese
+     */
+    PORTUGUESE = 22,
+
+    /**
+     * Russia
+     */
+    RUSSIA = 23,
+
+    /**
+     * Slovakia
+     */
+    SLOVAKIA = 24,
+
+    /**
+     * Spanish
+     */
+    SPANISH = 25,
+
+    /**
+     * Swedish
+     */
+    SWEDISH = 26,
+
+    /**
+     * Swiss (French)
+     */
+    SWISS_FRENCH = 27,
+
+    /**
+     * Swiss (German)
+     */
+    SWISS_GERMAN = 28,
+
+    /**
+     * Switzerland
+     */
+    SWITZERLAND = 29,
+
+    /**
+     * Taiwan
+     */
+    TAIWAN = 30,
+
+    /**
+     * Turkish_Q
+     */
+    TURKISH_Q = 31,
+
+    /**
+     * UK
+     */
+    UK = 32,
+
+    /**
+     * US
+     */
+    US = 33,
+
+    /**
+     * Yugoslavia
+     */
+    YUGOSLAVIA = 34,
+
+    /**
+     * Turkish_F
+     */
+    TURKISH_F = 35,
+}
\ No newline at end of file
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index c53811a..5aae37d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -10,6 +10,7 @@
 
 cc_test {
     name: "libinput_tests",
+    host_supported: true,
     srcs: [
         "IdGenerator_test.cpp",
         "InputChannel_test.cpp",
@@ -24,6 +25,7 @@
     static_libs: [
         "libgui_window_info_static",
         "libinput",
+        "libui-types",
     ],
     cflags: [
         "-Wall",
@@ -35,11 +37,13 @@
         "libbinder",
         "libcutils",
         "liblog",
-        "libui",
         "libutils",
         "libvintf",
     ],
     data: ["data/*"],
+    test_options: {
+        unit_test: true,
+    },
     test_suites: ["device-tests"],
 }
 
@@ -60,7 +64,6 @@
         "libcutils",
         "libutils",
         "libbinder",
-        "libui",
         "libbase",
     ],
 }
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index e872fa4..2344463 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -65,6 +65,9 @@
     }
 
     void SetUp() override {
+#if !defined(__ANDROID__)
+        GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device.";
+#endif
         loadKeyLayout("Generic");
         loadKeyCharacterMap("Generic");
     }
@@ -131,6 +134,9 @@
 }
 
 TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
+#if !defined(__ANDROID__)
+    GTEST_SKIP() << "Can't check kernel configs on host";
+#endif
     std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl";
     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
     ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
@@ -139,6 +145,9 @@
 }
 
 TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) {
+#if !defined(__ANDROID__)
+    GTEST_SKIP() << "Can't check kernel configs on host";
+#endif
     std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl";
     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
     ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath;
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 4a445de..bd12663 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -16,9 +16,10 @@
 
 #define LOG_TAG "VelocityTracker_test"
 
+#include <math.h>
 #include <array>
 #include <chrono>
-#include <math.h>
+#include <limits>
 
 #include <android-base/stringprintf.h>
 #include <attestation/HmacKeyManager.h>
@@ -64,8 +65,15 @@
     EXPECT_NEAR(actual, target, tolerance);
 }
 
-static void checkVelocity(float Vactual, float Vtarget) {
-    EXPECT_NEAR_BY_FRACTION(Vactual, Vtarget, VELOCITY_TOLERANCE);
+static void checkVelocity(std::optional<float> Vactual, std::optional<float> Vtarget) {
+    if (Vactual != std::nullopt) {
+        if (Vtarget == std::nullopt) {
+            FAIL() << "Expected no velocity, but found " << *Vactual;
+        }
+        EXPECT_NEAR_BY_FRACTION(*Vactual, *Vtarget, VELOCITY_TOLERANCE);
+    } else if (Vtarget != std::nullopt) {
+        FAIL() << "Expected  velocity, but found no velocity";
+    }
 }
 
 static void checkCoefficient(float actual, float target) {
@@ -86,7 +94,7 @@
     }
 };
 
-struct MotionEventEntry {
+struct PlanarMotionEventEntry {
     std::chrono::nanoseconds eventTime;
     std::vector<Position> positions;
 };
@@ -135,15 +143,43 @@
     return AMOTION_EVENT_ACTION_MOVE;
 }
 
-static std::vector<MotionEvent> createMotionEventStream(
-        const std::vector<MotionEventEntry>& motions) {
+static std::vector<MotionEvent> createAxisScrollMotionEventStream(
+        const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions) {
+    std::vector<MotionEvent> events;
+    for (const auto& [timeStamp, value] : motions) {
+        EXPECT_TRUE(!isnan(value)) << "The entry at pointerId must be valid";
+
+        PointerCoords coords[1];
+        coords[0].setAxisValue(AMOTION_EVENT_AXIS_SCROLL, value);
+
+        PointerProperties properties[1];
+        properties[0].id = DEFAULT_POINTER_ID;
+
+        MotionEvent event;
+        ui::Transform identityTransform;
+        event.initialize(InputEvent::nextId(), 5 /*deviceId*/, AINPUT_SOURCE_ROTARY_ENCODER,
+                         ADISPLAY_ID_NONE, INVALID_HMAC, AMOTION_EVENT_ACTION_SCROLL,
+                         0 /*actionButton*/, 0 /*flags*/, 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, identityTransform, 0 /*downTime*/,
+                         timeStamp.count(), 1 /*pointerCount*/, properties, coords);
+
+        events.emplace_back(event);
+    }
+
+    return events;
+}
+
+static std::vector<MotionEvent> createTouchMotionEventStream(
+        const std::vector<PlanarMotionEventEntry>& motions) {
     if (motions.empty()) {
         ADD_FAILURE() << "Need at least 1 sample to create a MotionEvent. Received empty vector.";
     }
 
     std::vector<MotionEvent> events;
     for (size_t i = 0; i < motions.size(); i++) {
-        const MotionEventEntry& entry = motions[i];
+        const PlanarMotionEventEntry& entry = motions[i];
         BitSet32 pointers = getValidPointers(entry.positions);
         const uint32_t pointerCount = pointers.count();
 
@@ -154,8 +190,8 @@
         } else if ((i == motions.size() - 1) && pointerCount == 1) {
             action = AMOTION_EVENT_ACTION_UP;
         } else {
-            const MotionEventEntry& previousEntry = motions[i-1];
-            const MotionEventEntry& nextEntry = motions[i+1];
+            const PlanarMotionEventEntry& previousEntry = motions[i-1];
+            const PlanarMotionEventEntry& nextEntry = motions[i+1];
             action = resolveAction(previousEntry.positions, entry.positions, nextEntry.positions);
         }
 
@@ -194,54 +230,197 @@
     return events;
 }
 
-static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
-                                    const std::vector<MotionEventEntry>& motions, int32_t axis,
-                                    float targetVelocity, uint32_t pointerId = DEFAULT_POINTER_ID) {
+static std::optional<float> computePlanarVelocity(
+        const VelocityTracker::Strategy strategy,
+        const std::vector<PlanarMotionEventEntry>& motions, int32_t axis,
+        uint32_t pointerId = DEFAULT_POINTER_ID) {
     VelocityTracker vt(strategy);
-    float Vx, Vy;
 
-    std::vector<MotionEvent> events = createMotionEventStream(motions);
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
 
-    vt.getVelocity(pointerId, &Vx, &Vy);
+    return vt.getVelocity(axis, pointerId);
+}
 
+static std::vector<MotionEvent> createMotionEventStream(
+        int32_t axis, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motion) {
     switch (axis) {
-    case AMOTION_EVENT_AXIS_X:
-        checkVelocity(Vx, targetVelocity);
-        break;
-    case AMOTION_EVENT_AXIS_Y:
-        checkVelocity(Vy, targetVelocity);
-        break;
-    default:
-        FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y";
+        case AMOTION_EVENT_AXIS_SCROLL:
+            return createAxisScrollMotionEventStream(motion);
+        default:
+            ADD_FAILURE() << "Axis " << axis << " is not supported";
+            return {};
     }
 }
 
-static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
-        const std::array<float, 3>& coefficients) {
+static std::optional<float> computeVelocity(
+        const VelocityTracker::Strategy strategy,
+        const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, int32_t axis) {
+    VelocityTracker vt(strategy);
+
+    for (const MotionEvent& event : createMotionEventStream(axis, motions)) {
+        vt.addMovement(&event);
+    }
+
+    return vt.getVelocity(axis, DEFAULT_POINTER_ID);
+}
+
+static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
+                                    const std::vector<PlanarMotionEventEntry>& motions,
+                                    int32_t axis, std::optional<float> targetVelocity,
+                                    uint32_t pointerId = DEFAULT_POINTER_ID) {
+    checkVelocity(computePlanarVelocity(strategy, motions, axis, pointerId), targetVelocity);
+}
+
+static void computeAndCheckAxisScrollVelocity(
+        const VelocityTracker::Strategy strategy,
+        const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions,
+        std::optional<float> targetVelocity) {
+    checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
+}
+
+static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEventEntry>& motions,
+                                             const std::array<float, 3>& coefficients) {
     VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
-    std::vector<MotionEvent> events = createMotionEventStream(motions);
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
-    VelocityTracker::Estimator estimator;
-    EXPECT_TRUE(vt.getEstimator(0, &estimator));
+    VelocityTracker::Estimator estimatorX;
+    VelocityTracker::Estimator estimatorY;
+    EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_X, 0, &estimatorX));
+    EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0, &estimatorY));
     for (size_t i = 0; i< coefficients.size(); i++) {
-        checkCoefficient(estimator.xCoeff[i], coefficients[i]);
-        checkCoefficient(estimator.yCoeff[i], coefficients[i]);
+        checkCoefficient(estimatorX.coeff[i], coefficients[i]);
+        checkCoefficient(estimatorY.coeff[i], coefficients[i]);
     }
 }
 
 /*
  * ================== VelocityTracker tests generated manually =====================================
  */
+TEST_F(VelocityTrackerTest, TestDefaultStrategiesForPlanarAxes) {
+    std::vector<PlanarMotionEventEntry> motions = {{10ms, {{2, 4}}},
+                                                   {20ms, {{4, 12}}},
+                                                   {30ms, {{6, 20}}},
+                                                   {40ms, {{10, 30}}}};
+
+    EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X),
+              computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions,
+                                    AMOTION_EVENT_AXIS_X));
+    EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y),
+              computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions,
+                                    AMOTION_EVENT_AXIS_Y));
+}
+
+TEST_F(VelocityTrackerTest, TestComputedVelocity) {
+    VelocityTracker::ComputedVelocity computedVelocity;
+
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 0 /*id*/, 200 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/, 400 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/, 650 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, 750 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 0 /*id*/, 1000 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/, 2000 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/, 3000 /*velocity*/);
+    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, 4000 /*velocity*/);
+
+    // Check the axes/indices with velocity.
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 0U /*id*/)), 200);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/)), 400);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/)), 650);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID)), 750);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 0U /*id*/)), 1000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/)), 2000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/)), 3000);
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID)), 4000);
+    for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
+        // Since no data was added for AXIS_SCROLL, expect empty value for the axis for any id.
+        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, id))
+                << "Empty scroll data expected at id=" << id;
+        if (id == 0 || id == 26U || id == 27U || id == MAX_POINTER_ID) {
+            // Already checked above; continue.
+            continue;
+        }
+        // No data was added to X/Y for this id, expect empty value.
+        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id))
+                << "Empty X data expected at id=" << id;
+        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, id))
+                << "Empty Y data expected at id=" << id;
+    }
+    // Out-of-bounds ids should given empty values.
+    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, -1));
+    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID + 1));
+}
+
+TEST_F(VelocityTrackerTest, TestGetComputedVelocity) {
+    std::vector<PlanarMotionEventEntry> motions = {
+            {235089067457000ns, {{528.00, 0}}}, {235089084684000ns, {{527.00, 0}}},
+            {235089093349000ns, {{527.00, 0}}}, {235089095677625ns, {{527.00, 0}}},
+            {235089101859000ns, {{527.00, 0}}}, {235089110378000ns, {{528.00, 0}}},
+            {235089112497111ns, {{528.25, 0}}}, {235089118760000ns, {{531.00, 0}}},
+            {235089126686000ns, {{535.00, 0}}}, {235089129316820ns, {{536.33, 0}}},
+            {235089135199000ns, {{540.00, 0}}}, {235089144297000ns, {{546.00, 0}}},
+            {235089146136443ns, {{547.21, 0}}}, {235089152923000ns, {{553.00, 0}}},
+            {235089160784000ns, {{559.00, 0}}}, {235089162955851ns, {{560.66, 0}}},
+            {235089162955851ns, {{560.66, 0}}}, // ACTION_UP
+    };
+    VelocityTracker vt(VelocityTracker::Strategy::IMPULSE);
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
+    for (const MotionEvent& event : events) {
+        vt.addMovement(&event);
+    }
+
+    float maxFloat = std::numeric_limits<float>::max();
+    VelocityTracker::ComputedVelocity computedVelocity;
+    computedVelocity = vt.getComputedVelocity(1000 /* units */, maxFloat);
+    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
+                  764.345703);
+
+    // Expect X velocity to be scaled with respective to provided units.
+    computedVelocity = vt.getComputedVelocity(1000000 /* units */, maxFloat);
+    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
+                  764345.703);
+
+    // Expect X velocity to be clamped by provided max velocity.
+    computedVelocity = vt.getComputedVelocity(1000000 /* units */, 1000);
+    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), 1000);
+
+    // All 0 data for Y; expect 0 velocity.
+    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID)), 0);
+
+    // No data for scroll-axis; expect empty velocity.
+    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, DEFAULT_POINTER_ID));
+}
+
+TEST_F(VelocityTrackerTest, TestApiInteractionsWithNoMotionEvents) {
+    VelocityTracker vt(VelocityTracker::Strategy::DEFAULT);
+
+    EXPECT_FALSE(vt.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
+
+    VelocityTracker::Estimator estimator;
+    EXPECT_FALSE(vt.getEstimator(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID, &estimator));
+
+    VelocityTracker::ComputedVelocity computedVelocity = vt.getComputedVelocity(1000, 1000);
+    for (uint32_t id = 0; id <= MAX_POINTER_ID; id++) {
+        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id));
+        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, id));
+    }
+
+    EXPECT_EQ(-1, vt.getActivePointerId());
+
+    // Make sure that the clearing functions execute without an issue.
+    vt.clearPointers(BitSet32(7U));
+    vt.clear();
+}
+
 TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) {
     // Same coordinate is reported 2 times in a row
     // It is difficult to determine the correct answer here, but at least the direction
     // of the reported velocity should be positive.
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{273, 0}}},
             {12585us, {{293, 0}}},
             {14730us, {{293, 0}}},
@@ -253,7 +432,7 @@
 
 TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
     // Same coordinate is reported 3 times in a row
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{293, 0}}},
             {6132us, {{293, 0}}},
             {11283us, {{293, 0}}},
@@ -265,7 +444,7 @@
 
 TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
     // Fixed velocity at 5 points per 10 milliseconds
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP
     };
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
@@ -289,7 +468,7 @@
 // --------------- Recorded by hand on swordfish ---------------------------------------------------
 TEST_F(VelocityTrackerTest, SwordfishFlingDown) {
     // Recording of a fling on Swordfish that could cause a fling in the wrong direction
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{271, 96}} },
         { 16071042ns, {{269.786346, 106.922775}} },
         { 35648403ns, {{267.983063, 156.660034}} },
@@ -324,7 +503,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) {
     // Sailfish - fling up - slow - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235089067457000ns, {{528.00, 983.00}} },
         { 235089084684000ns, {{527.00, 981.00}} },
         { 235089093349000ns, {{527.00, 977.00}} },
@@ -356,7 +535,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) {
     // Sailfish - fling up - slow - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235110560704000ns, {{522.00, 1107.00}} },
         { 235110575764000ns, {{522.00, 1107.00}} },
         { 235110584385000ns, {{522.00, 1107.00}} },
@@ -385,7 +564,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) {
     // Sailfish - fling up - slow - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 792536237000ns, {{580.00, 1317.00}} },
         { 792541538987ns, {{580.63, 1311.94}} },
         { 792544613000ns, {{581.00, 1309.00}} },
@@ -419,7 +598,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) {
     // Sailfish - fling up - faster - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235160420675000ns, {{610.00, 1042.00}} },
         { 235160428220000ns, {{609.00, 1026.00}} },
         { 235160436544000ns, {{609.00, 1024.00}} },
@@ -453,7 +632,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) {
     // Sailfish - fling up - faster - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 847153808000ns, {{576.00, 1264.00}} },
         { 847171174000ns, {{576.00, 1262.00}} },
         { 847179640000ns, {{576.00, 1257.00}} },
@@ -479,7 +658,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) {
     // Sailfish - fling up - faster - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235200532789000ns, {{507.00, 1084.00}} },
         { 235200549221000ns, {{507.00, 1083.00}} },
         { 235200557841000ns, {{507.00, 1081.00}} },
@@ -505,7 +684,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) {
     // Sailfish - fling up - fast - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 920922149000ns, {{561.00, 1412.00}} },
         { 920930185000ns, {{559.00, 1377.00}} },
         { 920930262463ns, {{558.98, 1376.66}} },
@@ -534,7 +713,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) {
     // Sailfish - fling up - fast - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235247153233000ns, {{518.00, 1168.00}} },
         { 235247170452000ns, {{517.00, 1167.00}} },
         { 235247178908000ns, {{515.00, 1159.00}} },
@@ -557,7 +736,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) {
     // Sailfish - fling up - fast - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235302568736000ns, {{529.00, 1167.00}} },
         { 235302576644000ns, {{523.00, 1140.00}} },
         { 235302579395063ns, {{520.91, 1130.61}} },
@@ -578,7 +757,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) {
     // Sailfish - fling down - slow - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235655749552755ns, {{582.00, 432.49}} },
         { 235655750638000ns, {{582.00, 433.00}} },
         { 235655758865000ns, {{582.00, 440.00}} },
@@ -612,7 +791,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) {
     // Sailfish - fling down - slow - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235671152083370ns, {{485.24, 558.28}} },
         { 235671154126000ns, {{485.00, 559.00}} },
         { 235671162497000ns, {{484.00, 566.00}} },
@@ -646,7 +825,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) {
     // Sailfish - fling down - slow - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 170983201000ns, {{557.00, 533.00}} },
         { 171000668000ns, {{556.00, 534.00}} },
         { 171007359750ns, {{554.73, 535.27}} },
@@ -673,7 +852,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) {
     // Sailfish - fling down - faster - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235695280333000ns, {{558.00, 451.00}} },
         { 235695283971237ns, {{558.43, 454.45}} },
         { 235695289038000ns, {{559.00, 462.00}} },
@@ -703,7 +882,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) {
     // Sailfish - fling down - faster - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235709624766000ns, {{535.00, 579.00}} },
         { 235709642256000ns, {{534.00, 580.00}} },
         { 235709643350278ns, {{533.94, 580.06}} },
@@ -734,7 +913,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) {
     // Sailfish - fling down - faster - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235727628927000ns, {{540.00, 440.00}} },
         { 235727636810000ns, {{537.00, 454.00}} },
         { 235727646176000ns, {{536.00, 454.00}} },
@@ -763,7 +942,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) {
     // Sailfish - fling down - fast - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235762352849000ns, {{467.00, 286.00}} },
         { 235762360250000ns, {{443.00, 344.00}} },
         { 235762362787412ns, {{434.77, 363.89}} },
@@ -784,7 +963,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) {
     // Sailfish - fling down - fast - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235772487188000ns, {{576.00, 204.00}} },
         { 235772495159000ns, {{553.00, 236.00}} },
         { 235772503568000ns, {{551.00, 240.00}} },
@@ -805,7 +984,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) {
     // Sailfish - fling down - fast - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 507650295000ns, {{628.00, 233.00}} },
         { 507658234000ns, {{605.00, 269.00}} },
         { 507666784000ns, {{601.00, 274.00}} },
@@ -831,12 +1010,12 @@
 /**
  * ================== Multiple pointers ============================================================
  *
- * Three fingers quickly tap the screen. Since this is a tap, the velocities should be zero.
+ * Three fingers quickly tap the screen. Since this is a tap, the velocities should be empty.
  * If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be
  * part of the fitted data), this can cause large velocity values to be reported instead.
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0us,      {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} },
         { 10800us,  {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN
         { 10800us,  {{1063, 1128}, {682, 1318}, {397, 1747}} }, // POINTER_DOWN
@@ -845,12 +1024,14 @@
         { 272700us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} },
     };
 
-    // Velocity should actually be zero, but we expect 0.016 here instead.
-    // This is close enough to zero, and is likely caused by division by a very small number.
-    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
-    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, 0);
-    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
-    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            std::nullopt);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            std::nullopt);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            std::nullopt);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            std::nullopt);
 }
 
 /**
@@ -863,7 +1044,7 @@
  * affected by the liftoff.
  */
 TEST_F(VelocityTrackerTest, ShortDelayBeforeActionUp) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{10, 0}}}, {10ms, {{20, 0}}}, {20ms, {{30, 0}}}, {30ms, {{30, 0}}}, // ACTION_UP
     };
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
@@ -873,27 +1054,29 @@
 
 /**
  * The last movement of a single pointer is ACTION_UP. If there's a long delay between the last
- * ACTION_MOVE and the final ACTION_UP, velocity should be reported as zero because the pointer
+ * ACTION_MOVE and the final ACTION_UP, velocity should be reported as empty because the pointer
  * should be assumed to have stopped.
  */
 TEST_F(VelocityTrackerTest, LongDelayBeforeActionUp) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{10, 0}}},
             {10ms, {{20, 0}}},
             {20ms, {{30, 0}}},
             {3000ms, {{30, 0}}}, // ACTION_UP
     };
-    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
-    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            std::nullopt);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            std::nullopt);
 }
 
 /**
  * The last movement of a pointer is always ACTION_POINTER_UP or ACTION_UP. If there's a long delay
  * before ACTION_POINTER_UP event, the movement should be assumed to have stopped.
- * The final velocity should be reported as zero for all pointers.
+ * The final velocity should be reported as empty for all pointers.
  */
 TEST_F(VelocityTrackerTest, LongDelayBeforeActionPointerUp) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{10, 0}}},
             {10ms, {{20, 0}, {100, 0}}},
             {20ms, {{30, 0}, {200, 0}}},
@@ -901,13 +1084,17 @@
             {40ms, {{30, 0}, {400, 0}}},
             {3000ms, {{30, 0}}}, // ACTION_POINTER_UP
     };
-    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0,
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            std::nullopt,
                             /*pointerId*/ 0);
-    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0,
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            std::nullopt,
                             /*pointerId*/ 0);
-    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0,
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            std::nullopt,
                             /*pointerId*/ 1);
-    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0,
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            std::nullopt,
                             /*pointerId*/ 1);
 }
 
@@ -937,7 +1124,7 @@
  * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2).
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} }, // 0 s
         { 1ms, {{1, 1}} }, // 0.001 s
         { 2ms, {{1, 1}} }, // 0.002 s
@@ -955,7 +1142,7 @@
  * Straight line y = x :: the constant and quadratic coefficients are zero.
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{-2, -2}} },
         { 1ms, {{-1, -1}} },
         { 2ms, {{-0, -0}} },
@@ -973,7 +1160,7 @@
  * Parabola
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} },
         { 1ms, {{4, 4}} },
         { 2ms, {{8, 8}} },
@@ -991,7 +1178,7 @@
  * Parabola
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} },
         { 1ms, {{4, 4}} },
         { 2ms, {{9, 9}} },
@@ -1009,7 +1196,7 @@
  * Parabola :: y = x^2 :: the constant and linear coefficients are zero.
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{4, 4}} },
         { 1ms, {{1, 1}} },
         { 2ms, {{0, 0}} },
@@ -1023,4 +1210,114 @@
     computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 0E3, 1E6}));
 }
 
+// Recorded by hand on sailfish, but only the diffs are taken to test cumulative axis velocity.
+TEST_F(VelocityTrackerTest, AxisScrollVelocity) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {235089067457000ns, 0.00}, {235089084684000ns, -1.00}, {235089093349000ns, 0.00},
+            {235089095677625ns, 0.00}, {235089101859000ns, 0.00},  {235089110378000ns, 0.00},
+            {235089112497111ns, 0.25}, {235089118760000ns, 1.75},  {235089126686000ns, 4.00},
+            {235089129316820ns, 1.33}, {235089135199000ns, 3.67},  {235089144297000ns, 6.00},
+            {235089146136443ns, 1.21}, {235089152923000ns, 5.79},  {235089160784000ns, 6.00},
+            {235089162955851ns, 1.66},
+    };
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {764.345703});
+}
+
+// --------------- Recorded by hand on a Wear OS device using a rotating side button ---------------
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollDown) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {224598065152ns, -0.050100}, {224621871104ns, -0.133600}, {224645464064ns, -0.551100},
+            {224669171712ns, -0.801600}, {224687063040ns, -1.035400}, {224706691072ns, -0.484300},
+            {224738213888ns, -0.334000}, {224754401280ns, -0.083500},
+    };
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {-27.86});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollUp) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {269606010880ns, 0.050100}, {269626064896ns, 0.217100}, {269641973760ns, 0.267200},
+            {269658079232ns, 0.267200}, {269674217472ns, 0.267200}, {269690683392ns, 0.367400},
+            {269706133504ns, 0.551100}, {269722173440ns, 0.501000},
+    };
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {31.92});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollDown_ThenUp_ThenDown) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {2580534001664ns, -0.033400}, {2580549992448ns, -0.133600},
+            {2580566769664ns, -0.250500}, {2580581974016ns, -0.183700},
+            {2580597964800ns, -0.267200}, {2580613955584ns, -0.551100},
+            {2580635189248ns, -0.601200}, {2580661927936ns, -0.450900},
+            {2580683161600ns, -0.417500}, {2580705705984ns, -0.150300},
+            {2580722745344ns, -0.016700}, {2580786446336ns, 0.050100},
+            {2580801912832ns, 0.150300},  {2580822360064ns, 0.300600},
+            {2580838088704ns, 0.300600},  {2580854341632ns, 0.400800},
+            {2580869808128ns, 0.517700},  {2580886061056ns, 0.501000},
+            {2580905984000ns, 0.350700},  {2580921974784ns, 0.350700},
+            {2580937965568ns, 0.066800},  {2580974665728ns, 0.016700},
+            {2581034434560ns, -0.066800}, {2581049901056ns, -0.116900},
+            {2581070610432ns, -0.317300}, {2581086076928ns, -0.200400},
+            {2581101805568ns, -0.233800}, {2581118058496ns, -0.417500},
+            {2581134049280ns, -0.417500}, {2581150040064ns, -0.367400},
+            {2581166030848ns, -0.267200}, {2581181759488ns, -0.150300},
+            {2581199847424ns, -0.066800},
+    };
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {-9.73});
+}
+
+// ------------------------------- Hand generated test cases ---------------------------------------
+TEST_F(VelocityTrackerTest, TestDefaultStrategyForAxisScroll) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {10ms, 20},
+            {20ms, 25},
+            {30ms, 50},
+            {40ms, 100},
+    };
+
+    EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, motions,
+                              AMOTION_EVENT_AXIS_SCROLL),
+              computeVelocity(VelocityTracker::Strategy::DEFAULT, motions,
+                              AMOTION_EVENT_AXIS_SCROLL));
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_SimilarDifferentialValues) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ns, 2.12},  {3ns, 2.12},
+                                                                       {7ns, 2.12},  {8ns, 2.12},
+                                                                       {15ns, 2.12}, {18ns, 2.12}};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {1690236059.86});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_OnlyTwoValues) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ms, 5}, {2ms, 10}};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {10000});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_ConstantVelocity) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ms, 20}, {2ms, 20},
+                                                                       {3ms, 20}, {4ms, 20},
+                                                                       {5ms, 20}, {6ms, 20}};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {20000});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_NoMotion) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ns, 0}, {2ns, 0},
+                                                                       {3ns, 0}, {4ns, 0},
+                                                                       {5ns, 0}, {6ns, 0}};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {0});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_NoData) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, std::nullopt);
+}
+
 } // namespace android
diff --git a/libs/jpegrecoverymap/OWNERS b/libs/jpegrecoverymap/OWNERS
index 6ace354..133af5b 100644
--- a/libs/jpegrecoverymap/OWNERS
+++ b/libs/jpegrecoverymap/OWNERS
@@ -1,3 +1,4 @@
 arifdikici@google.com
+deakin@google.com
 dichenzhang@google.com
 kyslov@google.com
\ No newline at end of file
diff --git a/libs/math/OWNERS b/libs/math/OWNERS
index 72d33bc..82ae422 100644
--- a/libs/math/OWNERS
+++ b/libs/math/OWNERS
@@ -1,3 +1,5 @@
 mathias@google.com
 randolphs@google.com
 romainguy@google.com
+sumir@google.com
+jreck@google.com
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 76b85d6..60328e4 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -136,6 +136,7 @@
     }
 
     std::vector<DisplayConfigImpl> modesPerDisplay[size];
+    ui::DisplayConnectionType displayConnectionTypes[size];
     int numModes = 0;
     for (int i = 0; i < size; ++i) {
         const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
@@ -145,6 +146,7 @@
             status != OK) {
             return status;
         }
+        displayConnectionTypes[i] = staticInfo.connectionType;
 
         ui::DynamicDisplayInfo dynamicInfo;
         if (const status_t status =
@@ -168,8 +170,6 @@
         }
     }
 
-    const std::optional<PhysicalDisplayId> internalId =
-            SurfaceComposerClient::getInternalDisplayId();
     ui::Dataspace defaultDataspace;
     ui::PixelFormat defaultPixelFormat;
     ui::Dataspace wcgDataspace;
@@ -201,8 +201,9 @@
 
     for (size_t i = 0; i < size; ++i) {
         const PhysicalDisplayId id = ids[i];
-        const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
-                                                     : ADisplayType::DISPLAY_TYPE_EXTERNAL;
+        const ADisplayType type = (displayConnectionTypes[i] == ui::DisplayConnectionType::Internal)
+                ? ADisplayType::DISPLAY_TYPE_INTERNAL
+                : ADisplayType::DISPLAY_TYPE_EXTERNAL;
         const std::vector<DisplayConfigImpl>& configs = modesPerDisplay[i];
         memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
 
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index f3009dd..180dce9 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "AHardwareBuffer"
 
+#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_aidl.h>
 #include <vndk/hardware_buffer.h>
 
 #include <errno.h>
@@ -32,6 +34,9 @@
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <aidl/android/hardware/graphics/common/PixelFormat.h>
 
+// TODO: Better way to handle this
+#include "../binder/ndk/parcel_internal.h"
+
 static constexpr int kFdBufferSize = 128 * sizeof(int);  // 128 ints
 
 using namespace android;
@@ -412,6 +417,25 @@
     return OK;
 }
 
+binder_status_t AHardwareBuffer_readFromParcel(const AParcel* _Nonnull parcel,
+        AHardwareBuffer* _Nullable* _Nonnull outBuffer) {
+    if (!parcel || !outBuffer) return STATUS_BAD_VALUE;
+    auto buffer = sp<GraphicBuffer>::make();
+    status_t status = parcel->get()->read(*buffer);
+    if (status != STATUS_OK) return status;
+    *outBuffer = AHardwareBuffer_from_GraphicBuffer(buffer.get());
+    AHardwareBuffer_acquire(*outBuffer);
+    return STATUS_OK;
+}
+
+binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull buffer,
+        AParcel* _Nonnull parcel) {
+    const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+    if (!gb) return STATUS_BAD_VALUE;
+    if (!parcel) return STATUS_BAD_VALUE;
+    return parcel->get()->write(*gb);
+}
+
 // ----------------------------------------------------------------------------
 // VNDK functions
 // ----------------------------------------------------------------------------
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 731f989..b075080 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -220,6 +220,15 @@
     return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
 }
 
+int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) {
+    if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+        return -EINVAL;
+    }
+    return native_window_set_frame_rate(window, 0,
+            ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+            ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+}
+
 /**************************************************************************************************
  * vndk-stable
  **************************************************************************************************/
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index dd07319..3503a9e 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -61,6 +61,9 @@
 
     // Android O
     first_version: "26",
+    export_header_libs: [
+        "libnativewindow_ndk_headers",
+    ],
 }
 
 cc_library {
@@ -96,6 +99,8 @@
         "liblog",
         "libutils",
         "libui",
+        "libbinder",
+        "libbinder_ndk",
         "android.hardware.graphics.common@1.1",
     ],
 
diff --git a/libs/nativewindow/include/android/hardware_buffer_aidl.h b/libs/nativewindow/include/android/hardware_buffer_aidl.h
new file mode 100644
index 0000000..906d9c6
--- /dev/null
+++ b/libs/nativewindow/include/android/hardware_buffer_aidl.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file hardware_buffer_aidl.h
+ * @brief HardwareBuffer NDK AIDL glue code
+ */
+
+/**
+ * @addtogroup AHardwareBuffer
+ *
+ * Parcelable support for AHardwareBuffer. Can be used with libbinder_ndk
+ *
+ * @{
+ */
+
+#ifndef ANDROID_HARDWARE_BUFFER_AIDL_H
+#define ANDROID_HARDWARE_BUFFER_AIDL_H
+
+#include <android/binder_parcel.h>
+#include <android/hardware_buffer.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Read an AHardwareBuffer from a AParcel. The output buffer will have an
+ * initial reference acquired and will need to be released with
+ * AHardwareBuffer_release.
+ *
+ * Available since API level 34.
+ *
+ * \return STATUS_OK on success
+ *         STATUS_BAD_VALUE if the parcel or outBuffer is null, or if there's an
+ *                          issue deserializing (eg, corrupted parcel)
+ *         STATUS_BAD_TYPE if the parcel's current data position is not that of
+ *                         an AHardwareBuffer type
+ *         STATUS_NO_MEMORY if an allocation fails
+ */
+binder_status_t AHardwareBuffer_readFromParcel(const AParcel* _Nonnull parcel,
+        AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(34);
+
+/**
+ * Write an AHardwareBuffer to an AParcel.
+ *
+ * Available since API level 34.
+ *
+ * \return STATUS_OK on success.
+ *         STATUS_BAD_VALUE if either buffer or parcel is null, or if the AHardwareBuffer*
+ *                          fails to serialize (eg, internally corrupted)
+ *         STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is
+ *                          unable to allocate more
+ *         STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs
+ */
+binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull buffer,
+        AParcel* _Nonnull parcel) __INTRODUCED_IN(34);
+
+__END_DECLS
+
+// Only enable the AIDL glue helper if this is C++
+#ifdef __cplusplus
+
+namespace aidl::android::hardware {
+
+/**
+ * Wrapper class that enables interop with AIDL NDK generation
+ * Takes ownership of the AHardwareBuffer* given to it in reset() and will automatically
+ * destroy it in the destructor, similar to a smart pointer container
+ */
+class HardwareBuffer {
+public:
+    HardwareBuffer() noexcept {}
+    explicit HardwareBuffer(HardwareBuffer&& other) noexcept : mBuffer(other.release()) {}
+
+    ~HardwareBuffer() {
+        reset();
+    }
+
+    binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
+        reset();
+        return AHardwareBuffer_readFromParcel(parcel, &mBuffer);
+    }
+
+    binder_status_t writeToParcel(AParcel* _Nonnull parcel) const {
+        if (!mBuffer) {
+            return STATUS_BAD_VALUE;
+        }
+        return AHardwareBuffer_writeToParcel(mBuffer, parcel);
+    }
+
+    /**
+     * Destroys any currently owned AHardwareBuffer* and takes ownership of the given
+     * AHardwareBuffer*
+     *
+     * @param buffer The buffer to take ownership of
+     */
+    void reset(AHardwareBuffer* _Nullable buffer = nullptr) noexcept {
+        if (mBuffer) {
+            AHardwareBuffer_release(mBuffer);
+            mBuffer = nullptr;
+        }
+        mBuffer = buffer;
+    }
+
+    inline AHardwareBuffer* _Nullable operator-> () const { return mBuffer;  }
+    inline AHardwareBuffer* _Nullable get() const { return mBuffer; }
+    inline explicit operator bool () const { return mBuffer != nullptr; }
+
+    HardwareBuffer& operator=(HardwareBuffer&& other) noexcept {
+        reset(other.release());
+        return *this;
+    }
+
+    /**
+     * Stops managing any contained AHardwareBuffer*, returning it to the caller. Ownership
+     * is released.
+     * @return AHardwareBuffer* or null if this was empty
+     */
+    [[nodiscard]] AHardwareBuffer* _Nullable release() noexcept {
+        AHardwareBuffer* _Nullable ret = mBuffer;
+        mBuffer = nullptr;
+        return ret;
+    }
+
+private:
+    HardwareBuffer(const HardwareBuffer& other) = delete;
+    HardwareBuffer& operator=(const HardwareBuffer& other) = delete;
+
+    AHardwareBuffer* _Nullable mBuffer = nullptr;
+};
+
+} // aidl::android::hardware
+
+#endif // __cplusplus
+
+#endif // ANDROID_HARDWARE_BUFFER_AIDL_H
+
+/** @} */
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 20f4b52..281ec52 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -313,6 +313,8 @@
  * You can register for changes in the refresh rate using
  * \a AChoreographer_registerRefreshRateCallback.
  *
+ * See ANativeWindow_clearFrameRate().
+ *
  * Available since API level 31.
  *
  * \param window pointer to an ANativeWindow object.
@@ -342,6 +344,37 @@
         int8_t compatibility, int8_t changeFrameRateStrategy)
         __INTRODUCED_IN(31);
 
+/**
+ * Clears the frame rate which is set for this window.
+ *
+ * This is equivalent to calling
+ * ANativeWindow_setFrameRateWithChangeStrategy(window, 0, compatibility, changeFrameRateStrategy).
+ *
+ * Usage of this API won't introduce frame rate throttling,
+ * or affect other aspects of the application's frame production
+ * pipeline. However, because the system may change the display refresh rate,
+ * calls to this function may result in changes to Choreographer callback
+ * timings, and changes to the time interval at which the system releases
+ * buffers back to the application.
+ *
+ * Note that this only has an effect for windows presented on the display. If
+ * this ANativeWindow is consumed by something other than the system compositor,
+ * e.g. a media codec, this call has no effect.
+ *
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
+ * See ANativeWindow_setFrameRateWithChangeStrategy().
+ *
+ * Available since API level 34.
+ *
+ * \param window pointer to an ANativeWindow object.
+ *
+ * \return 0 for success, -EINVAL if the window value is invalid.
+ */
+int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
+        __INTRODUCED_IN(__ANDROID_API_U__);
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 79f49e1..c7745e6 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -235,8 +235,8 @@
     NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS         = 25,
     NATIVE_WINDOW_GET_COMPOSITOR_TIMING           = 26,
     NATIVE_WINDOW_GET_FRAME_TIMESTAMPS            = 27,
-    NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT          = 28,
-    NATIVE_WINDOW_GET_HDR_SUPPORT                 = 29,
+    /* 28, removed: NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT */
+    /* 29, removed: NATIVE_WINDOW_GET_HDR_SUPPORT */
     NATIVE_WINDOW_SET_USAGE64                     = ANATIVEWINDOW_PERFORM_SET_USAGE64,
     NATIVE_WINDOW_GET_CONSUMER_USAGE64            = 31,
     NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA  = 32,
@@ -988,15 +988,34 @@
             outDequeueReadyTime, outReleaseTime);
 }
 
-static inline int native_window_get_wide_color_support(
-    struct ANativeWindow* window, bool* outSupport) {
-    return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT,
-            outSupport);
+/* deprecated. Always returns 0 and outSupport holds true. Don't call. */
+static inline int native_window_get_wide_color_support (
+    struct ANativeWindow* window __UNUSED, bool* outSupport) __deprecated;
+
+/*
+   Deprecated(b/242763577): to be removed, this method should not be used
+   Surface support should not be tied to the display
+   Return true since most displays should have this support
+*/
+static inline int native_window_get_wide_color_support (
+    struct ANativeWindow* window __UNUSED, bool* outSupport) {
+    *outSupport = true;
+    return 0;
 }
 
-static inline int native_window_get_hdr_support(struct ANativeWindow* window,
+/* deprecated. Always returns 0 and outSupport holds true. Don't call. */
+static inline int native_window_get_hdr_support(struct ANativeWindow* window __UNUSED,
+                                                bool* outSupport) __deprecated;
+
+/*
+   Deprecated(b/242763577): to be removed, this method should not be used
+   Surface support should not be tied to the display
+   Return true since most displays should have this support
+*/
+static inline int native_window_get_hdr_support(struct ANativeWindow* window __UNUSED,
                                                 bool* outSupport) {
-    return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
+    *outSupport = true;
+    return 0;
 }
 
 static inline int native_window_get_consumer_usage(struct ANativeWindow* window,
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 6296f9a..ce108b6 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -14,6 +14,8 @@
     AHardwareBuffer_release;
     AHardwareBuffer_sendHandleToUnixSocket;
     AHardwareBuffer_unlock;
+    AHardwareBuffer_readFromParcel; # introduced=34
+    AHardwareBuffer_writeToParcel; # introduced=34
     ANativeWindowBuffer_getHardwareBuffer; # llndk
     ANativeWindow_OemStorageGet; # llndk
     ANativeWindow_OemStorageSet; # llndk
@@ -49,6 +51,7 @@
     ANativeWindow_setDequeueTimeout; # systemapi # introduced=30
     ANativeWindow_setFrameRate; # introduced=30
     ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
+    ANativeWindow_clearFrameRate; # introduced=UpsideDownCake
     ANativeWindow_setSharedBufferMode; # llndk
     ANativeWindow_setSwapInterval; # llndk
     ANativeWindow_setUsage; # llndk
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index f6f57dd..0540538 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -21,13 +21,15 @@
 
 cc_defaults {
     name: "librenderengine_defaults",
-    defaults: ["renderengine_defaults"],
+    defaults: [
+        "android.hardware.graphics.composer3-ndk_shared",
+        "renderengine_defaults",
+    ],
     cflags: [
         "-DGL_GLEXT_PROTOTYPES",
         "-DEGL_EGLEXT_PROTOTYPES",
     ],
     shared_libs: [
-        "android.hardware.graphics.composer3-V1-ndk",
         "libbase",
         "libcutils",
         "libEGL",
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index c7ad058..9d9cb6b 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -63,12 +63,13 @@
                         "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();
+ftl::Future<FenceResult> 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<FenceResult>>();
+    std::future<FenceResult> resultFuture = resultPromise->get_future();
     drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache,
                        std::move(bufferFence));
     return resultFuture;
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index 249fec5..afbe6cf 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -24,6 +24,7 @@
 cc_benchmark {
     name: "librenderengine_bench",
     defaults: [
+        "android.hardware.graphics.composer3-ndk_shared",
         "skia_deps",
         "surfaceflinger_defaults",
     ],
@@ -43,7 +44,6 @@
     ],
 
     shared_libs: [
-        "android.hardware.graphics.composer3-V1-ndk",
         "libbase",
         "libcutils",
         "libjnigraphics",
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 7bcfff5..d44eb46 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -80,16 +80,26 @@
     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 ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        LOG_ALWAYS_FATAL_IF(ids.empty(), "Failed to get any display!");
+        ui::Size resolution = ui::kEmptySize;
+        // find the largest display resolution
+        for (auto id : ids) {
+            auto displayToken = surfaceComposerClient->getPhysicalDisplayToken(id);
+            ui::DisplayMode displayMode;
+            if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
+                LOG_ALWAYS_FATAL("Failed to get active display mode!");
+            }
+            auto tw = displayMode.resolution.width;
+            auto th = displayMode.resolution.height;
+            LOG_ALWAYS_FATAL_IF(tw <= 0 || th <= 0, "Invalid display size!");
+            if (resolution.width * resolution.height <
+                displayMode.resolution.width * displayMode.resolution.height) {
+                resolution = displayMode.resolution;
+            }
         }
-        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);
+        width = static_cast<uint32_t>(resolution.width);
+        height = static_cast<uint32_t>(resolution.height);
     });
     return std::pair<uint32_t, uint32_t>(width, height);
 }
@@ -159,9 +169,10 @@
     };
     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));
+    sp<Fence> waitFence =
+            re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd())
+                    .get()
+                    .value();
     waitFence->waitForever(LOG_TAG);
     return texture;
 }
@@ -190,10 +201,10 @@
 
     // 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));
+        sp<Fence> waitFence = re.drawLayers(display, layers, outputBuffer, kUseFrameBufferCache,
+                                            base::unique_fd())
+                                      .get()
+                                      .value();
         waitFence->waitForever(LOG_TAG);
     }
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 9a5ff54..13f766c 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1081,14 +1081,14 @@
 }
 
 void GLESRenderEngine::drawLayersInternal(
-        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const std::shared_ptr<std::promise<FenceResult>>&& 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");
-        resultPromise->set_value({NO_ERROR, base::unique_fd()});
+        resultPromise->set_value(Fence::NO_FENCE);
         return;
     }
 
@@ -1103,7 +1103,7 @@
 
     if (buffer == nullptr) {
         ALOGE("No output buffer provided. Aborting GPU composition.");
-        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        resultPromise->set_value(base::unexpected(BAD_VALUE));
         return;
     }
 
@@ -1132,7 +1132,7 @@
             ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                   buffer->getBuffer()->handle);
             checkErrors();
-            resultPromise->set_value({fbo->getStatus(), base::unique_fd()});
+            resultPromise->set_value(base::unexpected(fbo->getStatus()));
             return;
         }
         setViewportAndProjection(display.physicalDisplay, display.clip);
@@ -1144,7 +1144,7 @@
             ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
                   buffer->getBuffer()->handle);
             checkErrors();
-            resultPromise->set_value({status, base::unique_fd()});
+            resultPromise->set_value(base::unexpected(status));
             return;
         }
     }
@@ -1178,7 +1178,7 @@
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't render first blur pass");
-                resultPromise->set_value({status, base::unique_fd()});
+                resultPromise->set_value(base::unexpected(status));
                 return;
             }
 
@@ -1201,7 +1201,7 @@
                 ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't bind native framebuffer");
-                resultPromise->set_value({status, base::unique_fd()});
+                resultPromise->set_value(base::unexpected(status));
                 return;
             }
 
@@ -1210,7 +1210,7 @@
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->getBuffer()->handle);
                 checkErrors("Can't render blur filter");
-                resultPromise->set_value({status, base::unique_fd()});
+                resultPromise->set_value(base::unexpected(status));
                 return;
             }
         }
@@ -1310,7 +1310,7 @@
             checkErrors();
             // Chances are, something illegal happened (either the caller passed
             // us bad parameters, or we messed up our shader generation).
-            resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+            resultPromise->set_value(base::unexpected(INVALID_OPERATION));
             return;
         }
         mLastDrawFence = nullptr;
@@ -1322,8 +1322,7 @@
     mPriorResourcesCleaned = false;
 
     checkErrors();
-    resultPromise->set_value({NO_ERROR, std::move(drawFence)});
-    return;
+    resultPromise->set_value(sp<Fence>::make(std::move(drawFence)));
 }
 
 void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 1d7c2ca..1ee5cba 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -31,6 +31,7 @@
 #include <renderengine/RenderEngine.h>
 #include <renderengine/private/Description.h>
 #include <sys/types.h>
+#include <ui/FenceResult.h>
 #include "GLShadowTexture.h"
 #include "ImageManager.h"
 
@@ -102,7 +103,7 @@
             EXCLUDES(mRenderingMutex);
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
     bool canSkipPostRenderCleanup() const override;
-    void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+    void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
                             const DisplaySettings& display,
                             const std::vector<LayerSettings>& layers,
                             const std::shared_ptr<ExternalTexture>& buffer,
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 3e7f69c..199392c 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -18,6 +18,7 @@
 #define SF_RENDERENGINE_H_
 
 #include <android-base/unique_fd.h>
+#include <ftl/future.h>
 #include <math/mat4.h>
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/ExternalTexture.h>
@@ -26,6 +27,7 @@
 #include <renderengine/LayerSettings.h>
 #include <stdint.h>
 #include <sys/types.h>
+#include <ui/FenceResult.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
 
@@ -68,7 +70,6 @@
 class Mesh;
 class Texture;
 struct RenderEngineCreationArgs;
-struct RenderEngineResult;
 
 namespace threaded {
 class RenderEngineThreaded;
@@ -158,12 +159,13 @@
     // parameter does nothing.
     // @param bufferFence Fence signalling that the buffer is ready to be drawn
     // to.
-    // @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);
+    // @return A future object of FenceResult indicating whether drawing was
+    // successful in async mode.
+    virtual ftl::Future<FenceResult> 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
@@ -237,7 +239,7 @@
     const RenderEngineType mRenderEngineType;
 
     virtual void drawLayersInternal(
-            const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+            const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
             const DisplaySettings& display, const std::vector<LayerSettings>& layers,
             const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
             base::unique_fd&& bufferFence) = 0;
@@ -327,13 +329,6 @@
             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 248bd65..e3ce85d 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -48,14 +48,13 @@
     MOCK_METHOD0(cleanupPostRender, void());
     MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
     MOCK_METHOD5(drawLayers,
-                 std::future<RenderEngineResult>(const DisplaySettings&,
-                                                 const std::vector<LayerSettings>&,
-                                                 const std::shared_ptr<ExternalTexture>&,
-                                                 const bool, base::unique_fd&&));
+                 ftl::Future<FenceResult>(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&&));
+                 void(const std::shared_ptr<std::promise<FenceResult>>&&, 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/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index db983a8..b9aa5ac 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -632,7 +632,7 @@
 };
 
 void SkiaRenderEngine::drawLayersInternal(
-        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
         const DisplaySettings& display, const std::vector<LayerSettings>& layers,
         const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
         base::unique_fd&& bufferFence) {
@@ -642,7 +642,7 @@
 
     if (buffer == nullptr) {
         ALOGE("No output buffer provided. Aborting GPU composition.");
-        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        resultPromise->set_value(base::unexpected(BAD_VALUE));
         return;
     }
 
@@ -675,7 +675,7 @@
     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
     if (dstCanvas == nullptr) {
         ALOGE("Cannot acquire canvas from Skia.");
-        resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+        resultPromise->set_value(base::unexpected(BAD_VALUE));
         return;
     }
 
@@ -1126,8 +1126,7 @@
     }
 
     base::unique_fd drawFence = flushAndSubmit(grContext);
-    resultPromise->set_value({NO_ERROR, std::move(drawFence)});
-    return;
+    resultPromise->set_value(sp<Fence>::make(std::move(drawFence)));
 }
 
 size_t SkiaRenderEngine::getMaxTextureSize() const {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index a5cd278..e7c5b8f 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -135,7 +135,7 @@
     void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
     void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                     const ShadowSettings& shadowSettings);
-    void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+    void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
                             const DisplaySettings& display,
                             const std::vector<LayerSettings>& layers,
                             const std::shared_ptr<ExternalTexture>& buffer,
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index bbab792..6f328d7 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -24,6 +24,7 @@
 cc_test {
     name: "librenderengine_test",
     defaults: [
+        "android.hardware.graphics.composer3-ndk_shared",
         "skia_deps",
         "surfaceflinger_defaults",
     ],
@@ -49,7 +50,6 @@
     ],
 
     shared_libs: [
-        "android.hardware.graphics.composer3-V1-ndk",
         "libbase",
         "libcutils",
         "libEGL",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d23063c..777d02f 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -37,7 +37,6 @@
 #include <condition_variable>
 #include <fstream>
 
-#include "../gl/GLESRenderEngine.h"
 #include "../skia/SkiaGLRenderEngine.h"
 #include "../threaded/RenderEngineThreaded.h"
 
@@ -108,73 +107,9 @@
     virtual std::string name() = 0;
     virtual renderengine::RenderEngine::RenderEngineType type() = 0;
     virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0;
-    virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() {
-        return nullptr;
-    }
     virtual bool useColorManagement() const = 0;
 };
 
-class GLESRenderEngineFactory : public RenderEngineFactory {
-public:
-    std::string name() override { return "GLESRenderEngineFactory"; }
-
-    renderengine::RenderEngine::RenderEngineType type() {
-        return renderengine::RenderEngine::RenderEngineType::GLES;
-    }
-
-    std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
-        return createGLESRenderEngine();
-    }
-
-    std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() {
-        renderengine::RenderEngineCreationArgs reCreationArgs =
-                renderengine::RenderEngineCreationArgs::Builder()
-                        .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
-                        .setImageCacheSize(1)
-                        .setUseColorManagerment(false)
-                        .setEnableProtectedContext(false)
-                        .setPrecacheToneMapperShaderOnly(false)
-                        .setSupportsBackgroundBlur(true)
-                        .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
-                        .setRenderEngineType(type())
-                        .setUseColorManagerment(useColorManagement())
-                        .build();
-        return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
-    }
-
-    bool useColorManagement() const override { return false; }
-};
-
-class GLESCMRenderEngineFactory : public RenderEngineFactory {
-public:
-    std::string name() override { return "GLESCMRenderEngineFactory"; }
-
-    renderengine::RenderEngine::RenderEngineType type() {
-        return renderengine::RenderEngine::RenderEngineType::GLES;
-    }
-
-    std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
-        return createGLESRenderEngine();
-    }
-
-    std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() override {
-        renderengine::RenderEngineCreationArgs reCreationArgs =
-                renderengine::RenderEngineCreationArgs::Builder()
-                        .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
-                        .setImageCacheSize(1)
-                        .setEnableProtectedContext(false)
-                        .setPrecacheToneMapperShaderOnly(false)
-                        .setSupportsBackgroundBlur(true)
-                        .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
-                        .setRenderEngineType(type())
-                        .setUseColorManagerment(useColorManagement())
-                        .build();
-        return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
-    }
-
-    bool useColorManagement() const override { return true; }
-};
-
 class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
 public:
     std::string name() override { return "SkiaGLRenderEngineFactory"; }
@@ -313,9 +248,6 @@
         }
         for (uint32_t texName : mTexNames) {
             mRE->deleteTextures(1, &texName);
-            if (mGLESRE != nullptr) {
-                EXPECT_FALSE(mGLESRE->isTextureNameKnownForTesting(texName));
-            }
         }
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
@@ -528,20 +460,15 @@
 
     void invokeDraw(const renderengine::DisplaySettings& settings,
                     const std::vector<renderengine::LayerSettings>& layers) {
-        std::future<renderengine::RenderEngineResult> result =
+        ftl::Future<FenceResult> future =
                 mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+        ASSERT_TRUE(future.valid());
 
-        ASSERT_TRUE(result.valid());
-        auto [status, fence] = result.get();
+        auto result = future.get();
+        ASSERT_TRUE(result.ok());
 
-        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()));
-        }
+        auto fence = result.value();
+        fence->waitForever(LOG_TAG);
     }
 
     void drawEmptyLayers() {
@@ -664,26 +591,13 @@
 
     std::unique_ptr<renderengine::RenderEngine> mRE;
     std::shared_ptr<renderengine::ExternalTexture> mBuffer;
-    // GLESRenderEngine for testing GLES-specific behavior.
-    // Owened by mRE, but this is downcasted.
-    renderengine::gl::GLESRenderEngine* mGLESRE = nullptr;
 
     std::vector<uint32_t> mTexNames;
 };
 
 void RenderEngineTest::initializeRenderEngine() {
     const auto& renderEngineFactory = GetParam();
-    if (renderEngineFactory->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        // Only GLESRenderEngine exposes test-only methods. Provide a pointer to the
-        // GLESRenderEngine if we're using it so that we don't need to dynamic_cast
-        // every time.
-        std::unique_ptr<renderengine::gl::GLESRenderEngine> renderEngine =
-                renderEngineFactory->createGLESRenderEngine();
-        mGLESRE = renderEngine.get();
-        mRE = std::move(renderEngine);
-    } else {
-        mRE = renderEngineFactory->createRenderEngine();
-    }
+    mRE = renderEngineFactory->createRenderEngine();
     mBuffer = allocateDefaultBuffer();
 }
 
@@ -1004,9 +918,9 @@
     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.sourceDataspace = sourceDataspace;
     layer.alpha = 1.0f;
 
     // construct a fake color matrix
@@ -1032,13 +946,13 @@
 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};
+    dataspaceToColorMap[ui::Dataspace::V0_BT709] = {77, 0, 0, 255};
+    dataspaceToColorMap[ui::Dataspace::BT2020] = {101, 0, 0, 255};
+    dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {75, 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};
+    dataspaceToColorMap[customizedDataspace] = {61, 0, 0, 255};
     for (const auto& [sourceDataspace, color] : dataspaceToColorMap) {
         fillBufferWithColorTransformAndSourceDataspace<SourceVariant>(sourceDataspace);
         expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
@@ -1078,13 +992,13 @@
 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};
+    dataspaceToColorMap[ui::Dataspace::V0_BT709] = {198, 0, 0, 255};
+    dataspaceToColorMap[ui::Dataspace::BT2020] = {187, 0, 0, 255};
+    dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {192, 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};
+    dataspaceToColorMap[customizedDataspace] = {205, 0, 0, 255};
     for (const auto& [outputDataspace, color] : dataspaceToColorMap) {
         fillBufferWithColorTransformAndOutputDataspace<SourceVariant>(outputDataspace);
         expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
@@ -1600,9 +1514,7 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
-                         testing::Values(std::make_shared<GLESRenderEngineFactory>(),
-                                         std::make_shared<GLESCMRenderEngineFactory>(),
-                                         std::make_shared<SkiaGLESRenderEngineFactory>(),
+                         testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
                                          std::make_shared<SkiaGLESCMRenderEngineFactory>()));
 
 TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
@@ -1611,12 +1523,6 @@
 }
 
 TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) {
-    const auto& renderEngineFactory = GetParam();
-    if (renderEngineFactory->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        // GLES-specific test
-        return;
-    }
-
     initializeRenderEngine();
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
@@ -1681,49 +1587,13 @@
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layers.push_back(layer);
-    std::future<renderengine::RenderEngineResult> result =
+    ftl::Future<FenceResult> future =
             mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd());
 
-    ASSERT_TRUE(result.valid());
-    auto [status, fence] = result.get();
-    ASSERT_EQ(BAD_VALUE, status);
-    ASSERT_FALSE(fence.ok());
-}
-
-TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
-    const auto& renderEngineFactory = GetParam();
-
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        // GLES-specific test
-        return;
-    }
-
-    initializeRenderEngine();
-
-    renderengine::DisplaySettings settings;
-    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
-    settings.physicalDisplay = fullscreenRect();
-    settings.clip = fullscreenRect();
-
-    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);
-
-    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);
+    ASSERT_TRUE(future.valid());
+    auto result = future.get();
+    ASSERT_FALSE(result.ok());
+    ASSERT_EQ(BAD_VALUE, result.error());
 }
 
 TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
@@ -1785,11 +1655,7 @@
     const auto& renderEngineFactory = GetParam();
     // skip for non color management
     if (!renderEngineFactory->useColorManagement()) {
-        return;
-    }
-    // skip for GLESRenderEngine
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
+        GTEST_SKIP();
     }
 
     initializeRenderEngine();
@@ -1800,11 +1666,7 @@
     const auto& renderEngineFactory = GetParam();
     // skip for non color management
     if (!renderEngineFactory->useColorManagement()) {
-        return;
-    }
-    // skip for GLESRenderEngine
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
+        GTEST_SKIP();
     }
 
     initializeRenderEngine();
@@ -1895,11 +1757,7 @@
     const auto& renderEngineFactory = GetParam();
     // skip for non color management
     if (!renderEngineFactory->useColorManagement()) {
-        return;
-    }
-    // skip for GLESRenderEngine
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
+        GTEST_SKIP();
     }
 
     initializeRenderEngine();
@@ -1910,11 +1768,7 @@
     const auto& renderEngineFactory = GetParam();
     // skip for non color management
     if (!renderEngineFactory->useColorManagement()) {
-        return;
-    }
-    // skip for GLESRenderEngine
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
+        GTEST_SKIP();
     }
 
     initializeRenderEngine();
@@ -2005,11 +1859,7 @@
     const auto& renderEngineFactory = GetParam();
     // skip for non color management
     if (!renderEngineFactory->useColorManagement()) {
-        return;
-    }
-    // skip for GLESRenderEngine
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
+        GTEST_SKIP();
     }
 
     initializeRenderEngine();
@@ -2020,11 +1870,7 @@
     const auto& renderEngineFactory = GetParam();
     // skip for non color management
     if (!renderEngineFactory->useColorManagement()) {
-        return;
-    }
-    // skip for GLESRenderEngine
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
+        GTEST_SKIP();
     }
 
     initializeRenderEngine();
@@ -2219,20 +2065,20 @@
     layer.alpha = 1.0;
     layers.push_back(layer);
 
-    std::future<renderengine::RenderEngineResult> resultOne =
+    ftl::Future<FenceResult> futureOne =
             mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
-    ASSERT_TRUE(resultOne.valid());
-    auto [statusOne, fenceOne] = resultOne.get();
-    ASSERT_EQ(NO_ERROR, statusOne);
+    ASSERT_TRUE(futureOne.valid());
+    auto resultOne = futureOne.get();
+    ASSERT_TRUE(resultOne.ok());
+    auto fenceOne = resultOne.value();
 
-    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);
-    }
+    ftl::Future<FenceResult> futureTwo =
+            mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(fenceOne->dup()));
+    ASSERT_TRUE(futureTwo.valid());
+    auto resultTwo = futureTwo.get();
+    ASSERT_TRUE(resultTwo.ok());
+    auto fenceTwo = resultTwo.value();
+    fenceTwo->waitForever(LOG_TAG);
 
     // Only cleanup the first time.
     EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
@@ -2539,10 +2385,6 @@
 }
 
 TEST_P(RenderEngineTest, testDimming) {
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        GTEST_SKIP();
-    }
-
     initializeRenderEngine();
 
     const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -2615,9 +2457,6 @@
 }
 
 TEST_P(RenderEngineTest, testDimming_inGammaSpace) {
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        GTEST_SKIP();
-    }
     initializeRenderEngine();
 
     const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 |
@@ -2693,9 +2532,6 @@
 }
 
 TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) {
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        GTEST_SKIP();
-    }
     initializeRenderEngine();
 
     const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 |
@@ -2756,9 +2592,6 @@
 }
 
 TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_deviceHandles) {
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        GTEST_SKIP();
-    }
     initializeRenderEngine();
 
     const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 |
@@ -2821,9 +2654,6 @@
 
 TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
     initializeRenderEngine();
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
-    }
 
     const auto displayRect = Rect(2, 1);
     const renderengine::DisplaySettings display{
@@ -2929,10 +2759,6 @@
         GTEST_SKIP();
     }
 
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        GTEST_SKIP();
-    }
-
     initializeRenderEngine();
 
     tonemap(
@@ -2950,10 +2776,6 @@
         GTEST_SKIP();
     }
 
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        GTEST_SKIP();
-    }
-
     initializeRenderEngine();
 
     tonemap(
@@ -2967,10 +2789,6 @@
 }
 
 TEST_P(RenderEngineTest, r8_behaves_as_mask) {
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
-    }
-
     initializeRenderEngine();
 
     const auto r8Buffer = allocateR8Buffer(2, 1);
@@ -3028,10 +2846,6 @@
 }
 
 TEST_P(RenderEngineTest, r8_respects_color_transform) {
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
-    }
-
     initializeRenderEngine();
 
     const auto r8Buffer = allocateR8Buffer(2, 1);
@@ -3094,10 +2908,6 @@
 }
 
 TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) {
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        return;
-    }
-
     initializeRenderEngine();
 
     const auto r8Buffer = allocateR8Buffer(2, 1);
@@ -3163,10 +2973,6 @@
 }
 
 TEST_P(RenderEngineTest, primeShaderCache) {
-    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
-        GTEST_SKIP();
-    }
-
     initializeRenderEngine();
 
     auto fut = mRE->primeCache();
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 909ded3..1a96289 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -183,20 +183,17 @@
     base::unique_fd bufferFence;
 
     EXPECT_CALL(*mRenderEngine, drawLayersInternal)
-            .WillOnce([&](const std::shared_ptr<std::promise<renderengine::RenderEngineResult>>&&
-                                  resultPromise,
+            .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& 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()});
-            });
+                          base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); });
 
-    std::future<renderengine::RenderEngineResult> result =
+    ftl::Future<FenceResult> future =
             mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
-    ASSERT_TRUE(result.valid());
-    auto [status, _] = result.get();
-    ASSERT_EQ(NO_ERROR, status);
+    ASSERT_TRUE(future.valid());
+    auto result = future.get();
+    ASSERT_TRUE(result.ok());
 }
 
 } // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 203bb54..b41e843 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -313,21 +313,21 @@
 }
 
 void RenderEngineThreaded::drawLayersInternal(
-        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+        const std::shared_ptr<std::promise<FenceResult>>&& 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()});
+    resultPromise->set_value(Fence::NO_FENCE);
     return;
 }
 
-std::future<RenderEngineResult> RenderEngineThreaded::drawLayers(
+ftl::Future<FenceResult> 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();
-    const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>();
-    std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
+    const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
+    std::future<FenceResult> resultFuture = resultPromise->get_future();
     int fd = bufferFence.release();
     {
         std::lock_guard lock(mThreadMutex);
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 1340902..bf2ebea 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -56,11 +56,11 @@
     void useProtectedContext(bool useProtectedContext) override;
     void cleanupPostRender() 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;
+    ftl::Future<FenceResult> 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;
@@ -73,7 +73,7 @@
     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,
+    void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
                             const DisplaySettings& display,
                             const std::vector<LayerSettings>& layers,
                             const std::shared_ptr<ExternalTexture>& buffer,
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index a6cacad..78f692b 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -22,12 +22,12 @@
 #include <cutils/native_handle.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
-#include <utils/Vector.h>
 #include <utils/Timers.h>
+#include <utils/Vector.h>
 
-#include <binder/Parcel.h>
 #include <binder/IInterface.h>
 #include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
 
 #include <sensor/Sensor.h>
 #include <sensor/ISensorEventConnection.h>
@@ -205,9 +205,10 @@
             if (resource == nullptr) {
                 return BAD_VALUE;
             }
+            native_handle_set_fdsan_tag(resource);
             sp<ISensorEventConnection> ch =
                     createSensorDirectConnection(opPackageName, size, type, format, resource);
-            native_handle_close(resource);
+            native_handle_close_with_tag(resource);
             native_handle_delete(resource);
             reply->writeStrongBinder(IInterface::asBinder(ch));
             return NO_ERROR;
diff --git a/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp b/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp
index 6f10a67..6a61d36 100644
--- a/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp
+++ b/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp
@@ -24,14 +24,14 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     FuzzedDataProvider fdp(data, size);
-    BitTube bittube(size);
+    sp<BitTube> bittube(new BitTube(size));
     Parcel parcel[5];
-    bittube.writeToParcel(parcel);
+    bittube->writeToParcel(parcel);
     sp<BitTube> tube(new BitTube(size));
-    bittube.sendObjects<uint8_t>(tube, data, size);
+    bittube->sendObjects<uint8_t>(tube, data, size);
     uint8_t recvData[size];
     for (int i = 0; i < size; i++) recvData[i] = fdp.ConsumeIntegral<uint8_t>();
-    bittube.recvObjects<uint8_t>(tube, recvData, size);
+    bittube->recvObjects<uint8_t>(tube, recvData, size);
 
     return 0;
 }
diff --git a/libs/sensor/fuzz/sensor_fuzzer/Android.bp b/libs/sensor/fuzz/sensor_fuzzer/Android.bp
index cb17484..685c549 100644
--- a/libs/sensor/fuzz/sensor_fuzzer/Android.bp
+++ b/libs/sensor/fuzz/sensor_fuzzer/Android.bp
@@ -16,6 +16,15 @@
  *
  *****************************************************************************
  */
+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_fuzz {
     name: "sensor_fuzzer",
     srcs: [
diff --git a/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp b/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp
index 129f430..0e110b7 100644
--- a/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp
+++ b/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp
@@ -26,8 +26,10 @@
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     FuzzedDataProvider fdp(data, size);
     struct sensor_t sensor_type;
-    sensor_type.name = fdp.ConsumeBytesAsString(MAX_STR_LEN).c_str();
-    sensor_type.vendor = fdp.ConsumeBytesAsString(MAX_STR_LEN).c_str();
+    std::string name = fdp.ConsumeBytesAsString(MAX_STR_LEN);
+    sensor_type.name = name.c_str();
+    std::string vendor = fdp.ConsumeBytesAsString(MAX_STR_LEN);
+    sensor_type.vendor = vendor.c_str();
     sensor_type.stringType = "";
     sensor_type.requiredPermission = "";
     sensor_type.version = fdp.ConsumeIntegral<int>();
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
index 6b936de..960f845 100644
--- a/libs/shaders/Android.bp
+++ b/libs/shaders/Android.bp
@@ -23,13 +23,14 @@
 
 cc_library_static {
     name: "libshaders",
-
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+        "android.hardware.graphics.composer3-ndk_shared",
+    ],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
 
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
-        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.graphics.common@1.2",
         "libnativewindow",
     ],
diff --git a/libs/shaders/tests/Android.bp b/libs/shaders/tests/Android.bp
index cf671bc..1e4f45a 100644
--- a/libs/shaders/tests/Android.bp
+++ b/libs/shaders/tests/Android.bp
@@ -23,6 +23,10 @@
 
 cc_test {
     name: "libshaders_test",
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+        "android.hardware.graphics.composer3-ndk_shared",
+    ],
     test_suites: ["device-tests"],
     srcs: [
         "shaders_test.cpp",
@@ -31,8 +35,6 @@
         "libtonemap_headers",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
-        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.graphics.common@1.2",
         "libnativewindow",
     ],
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
index 37c9824..8c8815d 100644
--- a/libs/tonemap/Android.bp
+++ b/libs/tonemap/Android.bp
@@ -23,13 +23,15 @@
 
 cc_library_static {
     name: "libtonemap",
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+        "android.hardware.graphics.composer3-ndk_shared",
+    ],
     vendor_available: true,
 
     local_include_dirs: ["include"],
 
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
-        "android.hardware.graphics.composer3-V1-ndk",
         "liblog",
         "libnativewindow",
     ],
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
index 58851b4..2abf515 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -23,6 +23,10 @@
 
 cc_test {
     name: "libtonemap_test",
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+        "android.hardware.graphics.composer3-ndk_shared",
+    ],
     test_suites: ["device-tests"],
     srcs: [
         "tonemap_test.cpp",
@@ -31,8 +35,6 @@
         "libtonemap_headers",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
-        "android.hardware.graphics.composer3-V1-ndk",
         "libnativewindow",
     ],
     static_libs: [
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index ca8adfa..d33dd34 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -152,6 +152,8 @@
     ],
 
     defaults: [
+        "android.hardware.graphics.allocator-ndk_shared",
+        "android.hardware.graphics.common-ndk_shared",
         "libui-defaults",
         // Uncomment the following line to enable VALIDATE_REGIONS traces
         //defaults: ["libui-validate-regions-defaults"],
@@ -161,8 +163,6 @@
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
-        "android.hardware.graphics.allocator-V1-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",
@@ -180,7 +180,6 @@
 
     export_shared_lib_headers: [
         "android.hardware.graphics.common@1.2",
-        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libgralloctypes",
     ],
diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
index 3306012..6ae27de 100644
--- a/libs/ui/DeviceProductInfo.cpp
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -14,44 +14,43 @@
  * limitations under the License.
  */
 
+#include <ftl/match.h>
 #include <ui/DeviceProductInfo.h>
 
 #include <android-base/stringprintf.h>
-#include <utils/Log.h>
-
-#define RETURN_IF_ERROR(op) \
-    if (const status_t status = (op); status != OK) return status;
 
 namespace android {
 
-using base::StringAppendF;
+std::string to_string(const DeviceProductInfo& info) {
+    using base::StringAppendF;
 
-void DeviceProductInfo::dump(std::string& result) const {
-    StringAppendF(&result, "{name=\"%s\", ", name.c_str());
-    StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data());
-    StringAppendF(&result, "productId=%s, ", productId.c_str());
+    std::string result;
+    StringAppendF(&result, "{name=\"%s\", ", info.name.c_str());
+    StringAppendF(&result, "manufacturerPnpId=%s, ", info.manufacturerPnpId.data());
+    StringAppendF(&result, "productId=%s, ", info.productId.c_str());
 
-    if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) {
-        StringAppendF(&result, "modelYear=%u, ", model->year);
-    } else if (const auto* manufactureWeekAndYear =
-                       std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) {
-        StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week);
-        StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year);
-    } else if (const auto* manufactureYear =
-                       std::get_if<ManufactureYear>(&manufactureOrModelDate)) {
-        StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year);
-    } else {
-        ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
-    }
+    ftl::match(
+            info.manufactureOrModelDate,
+            [&](DeviceProductInfo::ModelYear model) {
+                StringAppendF(&result, "modelYear=%u, ", model.year);
+            },
+            [&](DeviceProductInfo::ManufactureWeekAndYear manufacture) {
+                StringAppendF(&result, "manufactureWeek=%u, ", manufacture.week);
+                StringAppendF(&result, "manufactureYear=%d, ", manufacture.year);
+            },
+            [&](DeviceProductInfo::ManufactureYear manufacture) {
+                StringAppendF(&result, "manufactureYear=%d, ", manufacture.year);
+            });
 
     result.append("relativeAddress=[");
-    for (size_t i = 0; i < relativeAddress.size(); i++) {
+    for (size_t i = 0; i < info.relativeAddress.size(); i++) {
         if (i != 0) {
             result.append(", ");
         }
-        StringAppendF(&result, "%u", relativeAddress[i]);
+        StringAppendF(&result, "%u", info.relativeAddress[i]);
     }
     result.append("]}");
+    return result;
 }
 
 } // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 3732fee..429760f 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -465,7 +465,7 @@
         if (flattenWordCount == 13) {
             usage = (uint64_t(buf[12]) << 32) | uint32_t(buf[6]);
         } else {
-            usage = uint64_t(usage_deprecated);
+            usage = uint64_t(ANDROID_NATIVE_UNSIGNED_CAST(usage_deprecated));
         }
         native_handle* h =
                 native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts));
diff --git a/libs/ui/include/ui/ColorMode.h b/libs/ui/include/ui/ColorMode.h
new file mode 100644
index 0000000..a47eaed
--- /dev/null
+++ b/libs/ui/include/ui/ColorMode.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <ui/GraphicTypes.h>
+
+namespace android::ui {
+
+using ColorModes = std::vector<ColorMode>;
+
+inline bool isWideColorMode(ColorMode colorMode) {
+    switch (colorMode) {
+        case ColorMode::DISPLAY_P3:
+        case ColorMode::ADOBE_RGB:
+        case ColorMode::DCI_P3:
+        case ColorMode::BT2020:
+        case ColorMode::DISPLAY_BT2020:
+        case ColorMode::BT2100_PQ:
+        case ColorMode::BT2100_HLG:
+            return true;
+        case ColorMode::NATIVE:
+        case ColorMode::STANDARD_BT601_625:
+        case ColorMode::STANDARD_BT601_625_UNADJUSTED:
+        case ColorMode::STANDARD_BT601_525:
+        case ColorMode::STANDARD_BT601_525_UNADJUSTED:
+        case ColorMode::STANDARD_BT709:
+        case ColorMode::SRGB:
+            return false;
+    }
+}
+
+inline Dataspace pickDataspaceFor(ColorMode colorMode) {
+    switch (colorMode) {
+        case ColorMode::DISPLAY_P3:
+        case ColorMode::BT2100_PQ:
+        case ColorMode::BT2100_HLG:
+        case ColorMode::DISPLAY_BT2020:
+            return Dataspace::DISPLAY_P3;
+        default:
+            return Dataspace::V0_SRGB;
+    }
+}
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index 879e46f..4229cf1 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -61,8 +61,8 @@
     // address is unavailable.
     // For example, for HDMI connected device this will be the physical address.
     std::vector<uint8_t> relativeAddress;
-
-    void dump(std::string& result) const;
 };
 
+std::string to_string(const DeviceProductInfo&);
+
 } // namespace android
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 9120972..d0c03fe 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -17,9 +17,10 @@
 #pragma once
 
 #include <cstdint>
-#include <optional>
 #include <string>
 
+#include <ftl/optional.h>
+
 namespace android {
 
 // ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t.
@@ -68,7 +69,7 @@
 
 // DisplayId of a physical display, such as the internal display or externally connected display.
 struct PhysicalDisplayId : DisplayId {
-    static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) {
+    static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
         if (id.value & FLAG_VIRTUAL) {
             return std::nullopt;
         }
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/ui/include/ui/FenceResult.h
similarity index 63%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/ui/include/ui/FenceResult.h
index 9fac3e8..6d63fc9 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/ui/include/ui/FenceResult.h
@@ -14,10 +14,20 @@
  * limitations under the License.
  */
 
-package android.gui;
+#pragma once
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
+#include <android-base/expected.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Fence;
+
+using FenceResult = base::expected<sp<Fence>, status_t>;
+
+inline status_t fenceStatus(const FenceResult& fenceResult) {
+    return fenceResult.ok() ? NO_ERROR : fenceResult.error();
 }
+
+} // namespace android
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 57be686..dbe475b 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -270,7 +270,7 @@
 
     // Send a callback when a GraphicBuffer dies.
     //
-    // This is used for BufferStateLayer caching. GraphicBuffers are refcounted per process. When
+    // This is used for layer caching. GraphicBuffers are refcounted per process. When
     // A GraphicBuffer doesn't have any more sp<> in a process, it is destroyed. This causes
     // problems when trying to implicitcly cache across process boundaries. Ideally, both sides
     // of the cache would hold onto wp<> references. When an app dropped its sp<>, the GraphicBuffer
diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h
index 566e417..83da821 100644
--- a/libs/ui/include/ui/StaticDisplayInfo.h
+++ b/libs/ui/include/ui/StaticDisplayInfo.h
@@ -23,7 +23,7 @@
 
 namespace android::ui {
 
-enum class DisplayConnectionType { Internal, External };
+enum class DisplayConnectionType { Internal, External, ftl_last = External };
 
 // Immutable information about physical display.
 struct StaticDisplayInfo {
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
index c6eb3d1..760dbce 100644
--- a/libs/vibrator/include/vibrator/ExternalVibration.h
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -33,7 +33,6 @@
     ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs,
             sp<IExternalVibrationController> controller);
     virtual ~ExternalVibration() = default;
-    ExternalVibration(const ExternalVibration&) = default;
 
     bool operator==(const ExternalVibration&) const;
 
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index c1e935a..62cf255 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -12,7 +12,10 @@
     name: "libETC1",
     srcs: ["ETC1/etc1.cpp"],
     host_supported: true,
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     target: {
         android: {
@@ -37,6 +40,9 @@
     symbol_file: "libEGL.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libEGL_headers",
+    ],
 }
 
 ndk_library {
@@ -44,6 +50,9 @@
     symbol_file: "libGLESv1_CM.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv1_CM_headers",
+    ],
 }
 
 ndk_library {
@@ -51,6 +60,9 @@
     symbol_file: "libGLESv2.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv2_headers",
+    ],
 }
 
 ndk_library {
@@ -58,6 +70,9 @@
     symbol_file: "libGLESv3.map.txt",
     first_version: "18",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv3_headers",
+    ],
 }
 
 cc_defaults {
@@ -170,7 +185,11 @@
         "libEGL_getProcAddress",
         "libEGL_blobCache",
     ],
-    ldflags: ["-Wl,--exclude-libs=ALL,--Bsymbolic-functions"],
+    ldflags: [
+        "-Wl,--exclude-libs=libEGL_getProcAddress.a",
+        "-Wl,--exclude-libs=libEGL_blobCache.a",
+        "-Wl,--Bsymbolic-functions",
+    ],
     export_include_dirs: ["EGL/include"],
     stubs: {
         symbol_file: "libEGL.map.txt",
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index d38f2ef..f1122fd 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -68,9 +68,9 @@
 
 static TraceEventHandle addTraceEvent(
         PlatformMethods* /**platform*/, char phase, const unsigned char* /*category_group_enabled*/,
-        const char* name, unsigned long long /*id*/, double /*timestamp*/, int /*num_args*/,
-        const char** /*arg_names*/, const unsigned char* /*arg_types*/,
-        const unsigned long long* /*arg_values*/, unsigned char /*flags*/) {
+        const char* name, unsigned long long /*id*/, double /*timestamp*/, int num_args,
+        const char** arg_names, const unsigned char* /*arg_types*/,
+        const unsigned long long* arg_values, unsigned char /*flags*/) {
     switch (phase) {
         case 'B': {
             ATRACE_BEGIN(name);
@@ -84,6 +84,13 @@
             ATRACE_NAME(name);
             break;
         }
+       case 'C': {
+             for(int i=0; i<num_args ; i++)
+             {
+                 ATRACE_INT(arg_names[i],arg_values[i]);
+             }
+             break;
+        }
         default:
             // Could handle other event types here
             break;
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index efa67db..8348d6c 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -28,7 +28,7 @@
 // Cache size limits.
 static const size_t maxKeySize = 12 * 1024;
 static const size_t maxValueSize = 64 * 1024;
-static const size_t maxTotalSize = 2 * 1024 * 1024;
+static const size_t maxTotalSize = 32 * 1024 * 1024;
 
 // The time in seconds to wait before saving newly inserted cache entries.
 static const unsigned int deferredSaveDelay = 4;
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index fd4522e..e94b565 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -36,7 +36,14 @@
         return;
     }
 
-    const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    if (ids.empty()) {
+        fprintf(stderr, "Failed to get ID for any displays.\n");
+        return;
+    }
+
+    // display 0 is picked for now, can extend to support all displays if needed
+    const auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     if (displayToken == nullptr) {
         fprintf(stderr, "ERROR: no display\n");
         return;
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 29a0e4f..ddcd51f 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -85,12 +85,12 @@
         "libstatspull",
         "libstatssocket",
         "libutils",
-        "libui",
         "server_configurable_flags",
     ],
     static_libs: [
         "libattestation",
         "libpalmrejection",
+        "libui-types",
     ],
 }
 
@@ -139,6 +139,7 @@
         "InputListener.cpp",
         "InputReaderBase.cpp",
         "InputThread.cpp",
+        "NotifyArgs.cpp",
         "VibrationElement.cpp",
     ],
 }
@@ -148,10 +149,10 @@
     srcs: [":libinputflinger_base_sources"],
     shared_libs: [
         "libbase",
+        "libbinder",
         "libcutils",
         "libinput",
         "liblog",
-        "libui",
         "libutils",
     ],
     header_libs: [
@@ -190,7 +191,16 @@
         "libinputservice_test",
         "Bug-115739809",
         "StructLayout_test",
+
+        // native fuzzers
         "inputflinger_latencytracker_fuzzer",
+        "inputflinger_cursor_input_fuzzer",
+        "inputflinger_keyboard_input_fuzzer",
+        "inputflinger_multitouch_input_fuzzer",
+        "inputflinger_switch_input_fuzzer",
+        "inputflinger_input_reader_fuzzer",
+        "inputflinger_blocking_queue_fuzzer",
+        "inputflinger_input_classifier_fuzzer",
 
         // Java/Kotlin targets
         "CtsWindowManagerDeviceTestCases",
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index 8300e8a..fe37287 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_BLOCKING_QUEUE_H
-#define _UI_INPUT_BLOCKING_QUEUE_H
+#pragma once
 
 #include "android-base/thread_annotations.h"
 #include <condition_variable>
@@ -106,6 +105,4 @@
     std::vector<T> mQueue GUARDED_BY(mLock);
 };
 
-
 } // namespace android
-#endif
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
index 8aee39f..23b6f57 100644
--- a/services/inputflinger/InputCommonConverter.cpp
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -263,6 +263,8 @@
 static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) == common::Axis::GENERIC_14);
 static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) == common::Axis::GENERIC_15);
 static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) == common::Axis::GENERIC_16);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) ==
+              static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16));
 
 static common::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
     common::VideoFrame out;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index dce327e..d33b298 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -31,308 +31,34 @@
 
 namespace android {
 
-// --- NotifyConfigurationChangedArgs ---
-
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
-      : NotifyArgs(id, eventTime) {}
-
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
-        const NotifyConfigurationChangedArgs& other)
-      : NotifyArgs(other.id, other.eventTime) {}
-
-bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime;
+std::list<NotifyArgs>& operator+=(std::list<NotifyArgs>& keep, std::list<NotifyArgs>&& consume) {
+    keep.splice(keep.end(), consume);
+    return keep;
 }
 
-void NotifyConfigurationChangedArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyConfigurationChanged(this);
-}
+// --- InputListenerInterface ---
 
-// --- NotifyKeyArgs ---
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {};
+// explicit deduction guide (not needed as of C++20)
+template <typename... V>
+Visitor(V...) -> Visitor<V...>;
 
-NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
-                             uint32_t source, int32_t displayId, uint32_t policyFlags,
-                             int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
-                             int32_t metaState, nsecs_t downTime)
-      : NotifyArgs(id, eventTime),
-        deviceId(deviceId),
-        source(source),
-        displayId(displayId),
-        policyFlags(policyFlags),
-        action(action),
-        flags(flags),
-        keyCode(keyCode),
-        scanCode(scanCode),
-        metaState(metaState),
-        downTime(downTime),
-        readTime(readTime) {}
-
-NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other)
-      : NotifyArgs(other.id, other.eventTime),
-        deviceId(other.deviceId),
-        source(other.source),
-        displayId(other.displayId),
-        policyFlags(other.policyFlags),
-        action(other.action),
-        flags(other.flags),
-        keyCode(other.keyCode),
-        scanCode(other.scanCode),
-        metaState(other.metaState),
-        downTime(other.downTime),
-        readTime(other.readTime) {}
-
-bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
-            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
-            policyFlags == rhs.policyFlags && action == rhs.action && flags == rhs.flags &&
-            keyCode == rhs.keyCode && scanCode == rhs.scanCode && metaState == rhs.metaState &&
-            downTime == rhs.downTime;
-}
-
-void NotifyKeyArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyKey(this);
-}
-
-// --- NotifyMotionArgs ---
-
-NotifyMotionArgs::NotifyMotionArgs(
-        int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source,
-        int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
-        int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
-        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
-        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-        const std::vector<TouchVideoFrame>& videoFrames)
-      : NotifyArgs(id, eventTime),
-        deviceId(deviceId),
-        source(source),
-        displayId(displayId),
-        policyFlags(policyFlags),
-        action(action),
-        actionButton(actionButton),
-        flags(flags),
-        metaState(metaState),
-        buttonState(buttonState),
-        classification(classification),
-        edgeFlags(edgeFlags),
-        pointerCount(pointerCount),
-        xPrecision(xPrecision),
-        yPrecision(yPrecision),
-        xCursorPosition(xCursorPosition),
-        yCursorPosition(yCursorPosition),
-        downTime(downTime),
-        readTime(readTime),
-        videoFrames(videoFrames) {
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        this->pointerProperties[i].copyFrom(pointerProperties[i]);
-        this->pointerCoords[i].copyFrom(pointerCoords[i]);
-    }
-}
-
-NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
-      : NotifyArgs(other.id, other.eventTime),
-        deviceId(other.deviceId),
-        source(other.source),
-        displayId(other.displayId),
-        policyFlags(other.policyFlags),
-        action(other.action),
-        actionButton(other.actionButton),
-        flags(other.flags),
-        metaState(other.metaState),
-        buttonState(other.buttonState),
-        classification(other.classification),
-        edgeFlags(other.edgeFlags),
-        pointerCount(other.pointerCount),
-        xPrecision(other.xPrecision),
-        yPrecision(other.yPrecision),
-        xCursorPosition(other.xCursorPosition),
-        yCursorPosition(other.yCursorPosition),
-        downTime(other.downTime),
-        readTime(other.readTime),
-        videoFrames(other.videoFrames) {
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].copyFrom(other.pointerProperties[i]);
-        pointerCoords[i].copyFrom(other.pointerCoords[i]);
-    }
-}
-
-static inline bool isCursorPositionEqual(float lhs, float rhs) {
-    return (isnan(lhs) && isnan(rhs)) || lhs == rhs;
-}
-
-bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
-    bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
-            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
-            policyFlags == rhs.policyFlags && action == rhs.action &&
-            actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
-            buttonState == rhs.buttonState && classification == rhs.classification &&
-            edgeFlags == rhs.edgeFlags &&
-            pointerCount == rhs.pointerCount
-            // PointerProperties and PointerCoords are compared separately below
-            && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
-            isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
-            isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
-            downTime == rhs.downTime && videoFrames == rhs.videoFrames;
-    if (!equal) {
-        return false;
-    }
-
-    for (size_t i = 0; i < pointerCount; i++) {
-        equal =
-                pointerProperties[i] == rhs.pointerProperties[i]
-                && pointerCoords[i] == rhs.pointerCoords[i];
-        if (!equal) {
-            return false;
-        }
-    }
-    return true;
-}
-
-std::string NotifyMotionArgs::dump() const {
-    std::string coords;
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        if (!coords.empty()) {
-            coords += ", ";
-        }
-        coords += StringPrintf("{%" PRIu32 ": ", i);
-        coords +=
-                StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
-                             pointerCoords[i].getX(), pointerCoords[i].getY(),
-                             pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
-        const int32_t toolType = pointerProperties[i].toolType;
-        if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
-            coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
-        }
-        const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
-        const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
-        const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
-        if (major != 0 || minor != 0) {
-            coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
-                                   orientation);
-        }
-        coords += "}";
-    }
-    return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
-                        ", source=%s, action=%s, pointerCount=%" PRIu32
-                        " pointers=%s, flags=0x%08x)",
-                        id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
-                        MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
-                        flags);
-}
-
-void NotifyMotionArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyMotion(this);
-}
-
-// --- NotifySwitchArgs ---
-
-NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
-                                   uint32_t switchValues, uint32_t switchMask)
-      : NotifyArgs(id, eventTime),
-        policyFlags(policyFlags),
-        switchValues(switchValues),
-        switchMask(switchMask) {}
-
-NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other)
-      : NotifyArgs(other.id, other.eventTime),
-        policyFlags(other.policyFlags),
-        switchValues(other.switchValues),
-        switchMask(other.switchMask) {}
-
-bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && policyFlags == rhs.policyFlags &&
-            switchValues == rhs.switchValues && switchMask == rhs.switchMask;
-}
-
-void NotifySwitchArgs::notify(InputListenerInterface& listener) const {
-    listener.notifySwitch(this);
-}
-
-// --- NotifySensorArgs ---
-
-NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                                   InputDeviceSensorType sensorType,
-                                   InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
-                                   nsecs_t hwTimestamp, std::vector<float> values)
-      : NotifyArgs(id, eventTime),
-        deviceId(deviceId),
-        source(source),
-        sensorType(sensorType),
-        accuracy(accuracy),
-        accuracyChanged(accuracyChanged),
-        hwTimestamp(hwTimestamp),
-        values(std::move(values)) {}
-
-NotifySensorArgs::NotifySensorArgs(const NotifySensorArgs& other)
-      : NotifyArgs(other.id, other.eventTime),
-        deviceId(other.deviceId),
-        source(other.source),
-        sensorType(other.sensorType),
-        accuracy(other.accuracy),
-        accuracyChanged(other.accuracyChanged),
-        hwTimestamp(other.hwTimestamp),
-        values(other.values) {}
-
-bool NotifySensorArgs::operator==(const NotifySensorArgs rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && sensorType == rhs.sensorType &&
-            accuracy == rhs.accuracy && accuracyChanged == rhs.accuracyChanged &&
-            hwTimestamp == rhs.hwTimestamp && values == rhs.values;
-}
-
-void NotifySensorArgs::notify(InputListenerInterface& listener) const {
-    listener.notifySensor(this);
-}
-
-// --- NotifyVibratorStateArgs ---
-
-NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId,
-                                                 bool isOn)
-      : NotifyArgs(id, eventTime), deviceId(deviceId), isOn(isOn) {}
-
-NotifyVibratorStateArgs::NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other)
-      : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId), isOn(other.isOn) {}
-
-bool NotifyVibratorStateArgs::operator==(const NotifyVibratorStateArgs rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
-            isOn == rhs.isOn;
-}
-
-void NotifyVibratorStateArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyVibratorState(this);
-}
-
-// --- NotifyDeviceResetArgs ---
-
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
-      : NotifyArgs(id, eventTime), deviceId(deviceId) {}
-
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other)
-      : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId) {}
-
-bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId;
-}
-
-void NotifyDeviceResetArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyDeviceReset(this);
-}
-
-// --- NotifyPointerCaptureChangedArgs ---
-
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
-        int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
-      : NotifyArgs(id, eventTime), request(request) {}
-
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
-        const NotifyPointerCaptureChangedArgs& other)
-      : NotifyArgs(other.id, other.eventTime), request(other.request) {}
-
-bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request;
-}
-
-void NotifyPointerCaptureChangedArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyPointerCaptureChanged(this);
+void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
+    Visitor v{
+            [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(&args); },
+            [&](const NotifyKeyArgs& args) { notifyKey(&args); },
+            [&](const NotifyMotionArgs& args) { notifyMotion(&args); },
+            [&](const NotifySwitchArgs& args) { notifySwitch(&args); },
+            [&](const NotifySensorArgs& args) { notifySensor(&args); },
+            [&](const NotifyVibratorStateArgs& args) { notifyVibratorState(&args); },
+            [&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(&args); },
+            [&](const NotifyPointerCaptureChangedArgs& args) {
+                notifyPointerCaptureChanged(&args);
+            },
+    };
+    std::visit(v, generalArgs);
 }
 
 // --- QueuedInputListener ---
@@ -350,47 +76,47 @@
 void QueuedInputListener::notifyConfigurationChanged(
         const NotifyConfigurationChangedArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyConfigurationChangedArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyKeyArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyMotionArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifySwitchArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifySensor(const NotifySensorArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifySensorArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyVibratorStateArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyDeviceResetArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyPointerCaptureChangedArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::flush() {
-    for (const std::unique_ptr<NotifyArgs>& args : mArgsQueue) {
-        args->notify(mInnerListener);
+    for (const NotifyArgs& args : mArgsQueue) {
+        mInnerListener.notify(args);
     }
     mArgsQueue.clear();
 }
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index b40ab7f..1137193 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_MANAGER_H
-#define _UI_INPUT_MANAGER_H
+#pragma once
 
 /**
  * Native input manager.
@@ -130,5 +129,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_MANAGER_H
diff --git a/services/inputflinger/InputProcessor.cpp b/services/inputflinger/InputProcessor.cpp
index 0749441..02d62bf 100644
--- a/services/inputflinger/InputProcessor.cpp
+++ b/services/inputflinger/InputProcessor.cpp
@@ -22,6 +22,7 @@
 #include <android-base/stringprintf.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
+#include <input/Input.h>
 #include <inttypes.h>
 #include <log/log.h>
 #include <algorithm>
@@ -123,40 +124,38 @@
 
 // --- ClassifierEvent ---
 
-ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args)
-      : type(ClassifierEventType::MOTION), args(std::move(args)){};
-ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args)
-      : type(ClassifierEventType::DEVICE_RESET), args(std::move(args)){};
-ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args)
-      : type(type), args(std::move(args)){};
+ClassifierEvent::ClassifierEvent(const NotifyMotionArgs& args)
+      : type(ClassifierEventType::MOTION), args(args){};
 
-ClassifierEvent::ClassifierEvent(ClassifierEvent&& other)
-      : type(other.type), args(std::move(other.args)){};
+ClassifierEvent::ClassifierEvent(const NotifyDeviceResetArgs& args)
+      : type(ClassifierEventType::DEVICE_RESET), args(args){};
+
+ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::optional<NotifyArgs> args)
+      : type(type), args(args){};
 
 ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
     type = other.type;
-    args = std::move(other.args);
+    args = other.args;
     return *this;
 }
 
 ClassifierEvent ClassifierEvent::createHalResetEvent() {
-    return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
+    return ClassifierEvent(ClassifierEventType::HAL_RESET, std::nullopt);
 }
 
 ClassifierEvent ClassifierEvent::createExitEvent() {
-    return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
+    return ClassifierEvent(ClassifierEventType::EXIT, std::nullopt);
 }
 
 std::optional<int32_t> ClassifierEvent::getDeviceId() const {
     switch (type) {
         case ClassifierEventType::MOTION: {
-            NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
-            return motionArgs->deviceId;
+            const NotifyMotionArgs& motionArgs = std::get<NotifyMotionArgs>(*args);
+            return motionArgs.deviceId;
         }
         case ClassifierEventType::DEVICE_RESET: {
-            NotifyDeviceResetArgs* deviceResetArgs =
-                    static_cast<NotifyDeviceResetArgs*>(args.get());
-            return deviceResetArgs->deviceId;
+            const NotifyDeviceResetArgs& deviceResetArgs = std::get<NotifyDeviceResetArgs>(*args);
+            return deviceResetArgs.deviceId;
         }
         case ClassifierEventType::HAL_RESET: {
             return std::nullopt;
@@ -212,12 +211,12 @@
         bool halResponseOk = true;
         switch (event.type) {
             case ClassifierEventType::MOTION: {
-                NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
-                common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(*motionArgs);
+                NotifyMotionArgs& motionArgs = std::get<NotifyMotionArgs>(*event.args);
+                common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(motionArgs);
                 common::Classification classification;
                 ndk::ScopedAStatus response = mService->classify(motionEvent, &classification);
                 if (response.isOk()) {
-                    updateClassification(motionArgs->deviceId, motionArgs->eventTime,
+                    updateClassification(motionArgs.deviceId, motionArgs.eventTime,
                                          getMotionClassification(classification));
                 }
                 break;
@@ -307,8 +306,7 @@
         updateLastDownTime(args.deviceId, args.downTime);
     }
 
-    ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
-    enqueueEvent(std::move(event));
+    enqueueEvent(args);
     return getClassification(args.deviceId);
 }
 
@@ -328,7 +326,7 @@
         std::optional<int32_t> eventDeviceId = event.getDeviceId();
         return eventDeviceId && (*eventDeviceId == deviceId);
     });
-    enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
+    enqueueEvent(args);
 }
 
 void MotionClassifier::dump(std::string& dump) {
@@ -436,7 +434,13 @@
             mQueuedListener.notifyMotion(args);
         } else {
             NotifyMotionArgs newArgs(*args);
-            newArgs.classification = mMotionClassifier->classify(newArgs);
+            const MotionClassification newClassification = mMotionClassifier->classify(newArgs);
+            LOG_ALWAYS_FATAL_IF(args->classification != MotionClassification::NONE &&
+                                        newClassification != MotionClassification::NONE,
+                                "Conflicting classifications %s (new) and %s (old)!",
+                                motionClassificationToString(newClassification),
+                                motionClassificationToString(args->classification));
+            newArgs.classification = newClassification;
             mQueuedListener.notifyMotion(&newArgs);
         }
     } // release lock
diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h
index 261e012..f4d02b6 100644
--- a/services/inputflinger/InputProcessor.h
+++ b/services/inputflinger/InputProcessor.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_CLASSIFIER_H
-#define _UI_INPUT_CLASSIFIER_H
+#pragma once
 
 #include <android-base/thread_annotations.h>
 #include <future>
@@ -36,12 +35,12 @@
 
 struct ClassifierEvent {
     ClassifierEventType type;
-    std::unique_ptr<NotifyArgs> args;
+    std::optional<NotifyArgs> args;
 
-    ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args);
-    ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args);
-    ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args);
-    ClassifierEvent(ClassifierEvent&& other);
+    ClassifierEvent(ClassifierEventType type, std::optional<NotifyArgs> args);
+    ClassifierEvent(const NotifyMotionArgs& args);
+    ClassifierEvent(const NotifyDeviceResetArgs& args);
+    ClassifierEvent(ClassifierEvent&& other) = default;
     ClassifierEvent& operator=(ClassifierEvent&& other);
 
     // Convenience function to create a HAL_RESET event
@@ -288,4 +287,3 @@
 };
 
 } // namespace android
-#endif
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
new file mode 100644
index 0000000..b192ad7
--- /dev/null
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NotifyArgs"
+
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#include "NotifyArgs.h"
+
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <math.h>
+#include <utils/Trace.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+// --- NotifyConfigurationChangedArgs ---
+
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
+      : id(id), eventTime(eventTime) {}
+
+// --- NotifyKeyArgs ---
+
+NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                             uint32_t source, int32_t displayId, uint32_t policyFlags,
+                             int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+                             int32_t metaState, nsecs_t downTime)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        flags(flags),
+        keyCode(keyCode),
+        scanCode(scanCode),
+        metaState(metaState),
+        downTime(downTime),
+        readTime(readTime) {}
+
+// --- NotifyMotionArgs ---
+
+NotifyMotionArgs::NotifyMotionArgs(
+        int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source,
+        int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
+        int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
+        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
+        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+        const std::vector<TouchVideoFrame>& videoFrames)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        actionButton(actionButton),
+        flags(flags),
+        metaState(metaState),
+        buttonState(buttonState),
+        classification(classification),
+        edgeFlags(edgeFlags),
+        pointerCount(pointerCount),
+        xPrecision(xPrecision),
+        yPrecision(yPrecision),
+        xCursorPosition(xCursorPosition),
+        yCursorPosition(yCursorPosition),
+        downTime(downTime),
+        readTime(readTime),
+        videoFrames(videoFrames) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        this->pointerProperties[i].copyFrom(pointerProperties[i]);
+        this->pointerCoords[i].copyFrom(pointerCoords[i]);
+    }
+}
+
+NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
+      : id(other.id),
+        eventTime(other.eventTime),
+        deviceId(other.deviceId),
+        source(other.source),
+        displayId(other.displayId),
+        policyFlags(other.policyFlags),
+        action(other.action),
+        actionButton(other.actionButton),
+        flags(other.flags),
+        metaState(other.metaState),
+        buttonState(other.buttonState),
+        classification(other.classification),
+        edgeFlags(other.edgeFlags),
+        pointerCount(other.pointerCount),
+        xPrecision(other.xPrecision),
+        yPrecision(other.yPrecision),
+        xCursorPosition(other.xCursorPosition),
+        yCursorPosition(other.yCursorPosition),
+        downTime(other.downTime),
+        readTime(other.readTime),
+        videoFrames(other.videoFrames) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].copyFrom(other.pointerProperties[i]);
+        pointerCoords[i].copyFrom(other.pointerCoords[i]);
+    }
+}
+
+static inline bool isCursorPositionEqual(float lhs, float rhs) {
+    return (isnan(lhs) && isnan(rhs)) || lhs == rhs;
+}
+
+bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
+    bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+            policyFlags == rhs.policyFlags && action == rhs.action &&
+            actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
+            buttonState == rhs.buttonState && classification == rhs.classification &&
+            edgeFlags == rhs.edgeFlags &&
+            pointerCount == rhs.pointerCount
+            // PointerProperties and PointerCoords are compared separately below
+            && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
+            isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
+            isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
+            downTime == rhs.downTime && videoFrames == rhs.videoFrames;
+    if (!equal) {
+        return false;
+    }
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        equal = pointerProperties[i] == rhs.pointerProperties[i] &&
+                pointerCoords[i] == rhs.pointerCoords[i];
+        if (!equal) {
+            return false;
+        }
+    }
+    return true;
+}
+
+std::string NotifyMotionArgs::dump() const {
+    std::string coords;
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        if (!coords.empty()) {
+            coords += ", ";
+        }
+        coords += StringPrintf("{%" PRIu32 ": ", i);
+        coords +=
+                StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
+                             pointerCoords[i].getX(), pointerCoords[i].getY(),
+                             pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+        const int32_t toolType = pointerProperties[i].toolType;
+        if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
+            coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
+        }
+        const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+        const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+        const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+        if (major != 0 || minor != 0) {
+            coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
+                                   orientation);
+        }
+        coords += "}";
+    }
+    return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
+                        ", source=%s, action=%s, pointerCount=%" PRIu32
+                        " pointers=%s, flags=0x%08x)",
+                        id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
+                        MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
+                        flags);
+}
+
+// --- NotifySwitchArgs ---
+
+NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
+                                   uint32_t switchValues, uint32_t switchMask)
+      : id(id),
+        eventTime(eventTime),
+        policyFlags(policyFlags),
+        switchValues(switchValues),
+        switchMask(switchMask) {}
+
+// --- NotifySensorArgs ---
+
+NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                                   InputDeviceSensorType sensorType,
+                                   InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+                                   nsecs_t hwTimestamp, std::vector<float> values)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        sensorType(sensorType),
+        accuracy(accuracy),
+        accuracyChanged(accuracyChanged),
+        hwTimestamp(hwTimestamp),
+        values(std::move(values)) {}
+
+// --- NotifyVibratorStateArgs ---
+
+NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId,
+                                                 bool isOn)
+      : id(id), eventTime(eventTime), deviceId(deviceId), isOn(isOn) {}
+
+// --- NotifyDeviceResetArgs ---
+
+NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
+      : id(id), eventTime(eventTime), deviceId(deviceId) {}
+
+// --- NotifyPointerCaptureChangedArgs ---
+
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+        int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
+      : id(id), eventTime(eventTime), request(request) {}
+
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {};
+// explicit deduction guide (not needed as of C++20)
+template <typename... V>
+Visitor(V...) -> Visitor<V...>;
+
+const char* toString(const NotifyArgs& args) {
+    Visitor toStringVisitor{
+            [&](const NotifyConfigurationChangedArgs&) { return "NotifyConfigurationChangedArgs"; },
+            [&](const NotifyKeyArgs&) { return "NotifyKeyArgs"; },
+            [&](const NotifyMotionArgs&) { return "NotifyMotionArgs"; },
+            [&](const NotifySensorArgs&) { return "NotifySensorArgs"; },
+            [&](const NotifySwitchArgs&) { return "NotifySwitchArgs"; },
+            [&](const NotifyDeviceResetArgs&) { return "NotifyDeviceResetArgs"; },
+            [&](const NotifyPointerCaptureChangedArgs&) {
+                return "NotifyPointerCaptureChangedArgs";
+            },
+            [&](const NotifyVibratorStateArgs&) { return "NotifyVibratorStateArgs"; },
+    };
+    return std::visit(toStringVisitor, args);
+}
+
+} // namespace android
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index f673a28..4a0f2ec 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -38,6 +38,7 @@
       "name": "CtsViewTestCases",
       "options": [
         {
+          "include-filter": "android.view.cts.input",
           "include-filter": "android.view.cts.MotionEventTest",
           "include-filter": "android.view.cts.PointerCaptureTest",
           "include-filter": "android.view.cts.VerifyInputEventTest"
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 4e0f0c3..ec41025 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -77,8 +77,7 @@
 }
 
 static bool isFromTouchscreen(int32_t source) {
-    return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN) &&
-            !isFromSource(source, AINPUT_SOURCE_STYLUS);
+    return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN);
 }
 
 static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) {
@@ -99,17 +98,15 @@
     return false;
 }
 
-static int getLinuxToolType(int32_t toolType) {
-    switch (toolType) {
-        case AMOTION_EVENT_TOOL_TYPE_FINGER:
-            return MT_TOOL_FINGER;
-        case AMOTION_EVENT_TOOL_TYPE_STYLUS:
-            return MT_TOOL_PEN;
-        case AMOTION_EVENT_TOOL_TYPE_PALM:
-            return MT_TOOL_PALM;
+static int getLinuxToolCode(int toolType) {
+    if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+        return BTN_TOOL_PEN;
     }
-    ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType);
-    return MT_TOOL_FINGER;
+    if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
+        return BTN_TOOL_FINGER;
+    }
+    ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
+    return BTN_TOOL_FINGER;
 }
 
 static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
@@ -144,23 +141,6 @@
     return AMOTION_EVENT_ACTION_MOVE;
 }
 
-/**
- * Remove the data for the provided pointers from the args. The pointers are identified by their
- * pointerId, not by the index inside the array.
- * Return the new NotifyMotionArgs struct that has the remaining pointers.
- * The only fields that may be different in the returned args from the provided args are:
- *     - action
- *     - pointerCount
- *     - pointerProperties
- *     - pointerCoords
- * Action might change because it contains a pointer index. If another pointer is removed, the
- * active pointer index would be shifted.
- * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer
- * id is the acting pointer id.
- *
- * @param args the args from which the pointers should be removed
- * @param pointerIds the pointer ids of the pointers that should be removed
- */
 NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
                                   const std::set<int32_t>& pointerIds) {
     const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
@@ -206,6 +186,26 @@
     return newArgs;
 }
 
+/**
+ * Remove stylus pointers from the provided NotifyMotionArgs.
+ *
+ * Return NotifyMotionArgs where the stylus pointers have been removed.
+ * If this results in removal of the active pointer, then return nullopt.
+ */
+static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
+    std::set<int32_t> stylusPointerIds;
+    for (uint32_t i = 0; i < args.pointerCount; i++) {
+        if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+            stylusPointerIds.insert(args.pointerProperties[i].id);
+        }
+    }
+    NotifyMotionArgs withoutStylusPointers = removePointerIds(args, stylusPointerIds);
+    if (withoutStylusPointers.pointerCount == 0 || withoutStylusPointers.action == ACTION_UNKNOWN) {
+        return std::nullopt;
+    }
+    return withoutStylusPointers;
+}
+
 std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo(
         const InputDeviceInfo& deviceInfo) {
     if (!isFromTouchscreen(deviceInfo.getSources())) {
@@ -562,7 +562,7 @@
         touches.emplace_back(::ui::InProgressTouchEvdev());
         touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
         touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
-        touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType);
+        // The field 'tool_type' is not used for palm rejection
 
         // Whether there is new information for the touch.
         touches.back().altered = true;
@@ -609,15 +609,57 @@
 
         // The fields 'radius_x' and 'radius_x' are not used for palm rejection
         touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
-        touches.back().tool_code = BTN_TOOL_FINGER;
+        touches.back().tool_code = getLinuxToolCode(args.pointerProperties[i].toolType);
         // The field 'orientation' is not used for palm rejection
         // The fields 'tilt_x' and 'tilt_y' are not used for palm rejection
-        touches.back().reported_tool_type = ::ui::EventPointerType::kTouch;
+        // The field 'reported_tool_type' is not used for palm rejection
         touches.back().stylus_button = false;
     }
     return touches;
 }
 
+std::set<int32_t> PalmRejector::detectPalmPointers(const NotifyMotionArgs& args) {
+    std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold;
+    std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress;
+
+    // Store the slot state before we call getTouches and update it. This way, we can find
+    // the slots that have been removed due to the incoming event.
+    SlotState oldSlotState = mSlotState;
+    mSlotState.update(args);
+
+    std::vector<::ui::InProgressTouchEvdev> touches =
+            getTouches(args, mDeviceInfo, oldSlotState, mSlotState);
+    ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime);
+
+    if (DEBUG_MODEL) {
+        std::stringstream touchesStream;
+        for (const ::ui::InProgressTouchEvdev& touch : touches) {
+            touchesStream << touch.tracking_id << " : " << touch << "\n";
+        }
+        ALOGD("Filter: touches = %s", touchesStream.str().c_str());
+    }
+
+    mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress);
+
+    ALOGD_IF(DEBUG_MODEL, "Response: slotsToHold = %s, slotsToSuppress = %s",
+             slotsToHold.to_string().c_str(), slotsToSuppress.to_string().c_str());
+
+    // Now that we know which slots should be suppressed, let's convert those to pointer id's.
+    std::set<int32_t> newSuppressedIds;
+    for (size_t i = 0; i < args.pointerCount; i++) {
+        const int32_t pointerId = args.pointerProperties[i].id;
+        std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId);
+        if (!slot) {
+            slot = mSlotState.getSlotForPointerId(pointerId);
+            LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId);
+        }
+        if (slotsToSuppress.test(*slot)) {
+            newSuppressedIds.insert(pointerId);
+        }
+    }
+    return newSuppressedIds;
+}
+
 std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) {
     if (mPalmDetectionFilter == nullptr) {
         return {args};
@@ -635,42 +677,17 @@
     if (args.action == AMOTION_EVENT_ACTION_DOWN) {
         mSuppressedPointerIds.clear();
     }
-    std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold;
-    std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress;
 
-    // Store the slot state before we call getTouches and update it. This way, we can find
-    // the slots that have been removed due to the incoming event.
-    SlotState oldSlotState = mSlotState;
-    mSlotState.update(args);
-    std::vector<::ui::InProgressTouchEvdev> touches =
-            getTouches(args, mDeviceInfo, oldSlotState, mSlotState);
-    ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime);
-
-    if (DEBUG_MODEL) {
-        std::stringstream touchesStream;
-        for (const ::ui::InProgressTouchEvdev& touch : touches) {
-            touchesStream << touch.tracking_id << " : " << touch << "\n";
-        }
-        ALOGD("Filter: touches = %s", touchesStream.str().c_str());
-    }
-    mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress);
-
-    ALOGD_IF(DEBUG_MODEL, "Response: slotsToHold = %s, slotsToSuppress = %s",
-             slotsToHold.to_string().c_str(), slotsToSuppress.to_string().c_str());
-
-    // Now that we know which slots should be suppressed, let's convert those to pointer id's.
     std::set<int32_t> oldSuppressedIds;
     std::swap(oldSuppressedIds, mSuppressedPointerIds);
-    for (size_t i = 0; i < args.pointerCount; i++) {
-        const int32_t pointerId = args.pointerProperties[i].id;
-        std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId);
-        if (!slot) {
-            slot = mSlotState.getSlotForPointerId(pointerId);
-            LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId);
-        }
-        if (slotsToSuppress.test(*slot)) {
-            mSuppressedPointerIds.insert(pointerId);
-        }
+
+    std::optional<NotifyMotionArgs> touchOnlyArgs = removeStylusPointerIds(args);
+    if (touchOnlyArgs) {
+        mSuppressedPointerIds = detectPalmPointers(*touchOnlyArgs);
+    } else {
+        // This is a stylus-only event.
+        // We can skip this event and just keep the suppressed pointer ids the same as before.
+        mSuppressedPointerIds = oldSuppressedIds;
     }
 
     std::vector<NotifyMotionArgs> argsWithoutUnwantedPointers =
@@ -691,7 +708,7 @@
     return argsWithoutUnwantedPointers;
 }
 
-const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() {
+const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() const {
     return mDeviceInfo;
 }
 
diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h
index 8ff9658..5d0dde8 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.h
+++ b/services/inputflinger/UnwantedInteractionBlocker.h
@@ -43,6 +43,25 @@
 
 static constexpr int32_t ACTION_UNKNOWN = -1;
 
+/**
+ * Remove the data for the provided pointers from the args. The pointers are identified by their
+ * pointerId, not by the index inside the array.
+ * Return the new NotifyMotionArgs struct that has the remaining pointers.
+ * The only fields that may be different in the returned args from the provided args are:
+ *     - action
+ *     - pointerCount
+ *     - pointerProperties
+ *     - pointerCoords
+ * Action might change because it contains a pointer index. If another pointer is removed, the
+ * active pointer index would be shifted.
+ *
+ * If the active pointer id is removed (for example, for events like
+ * POINTER_UP or POINTER_DOWN), then the action is set to ACTION_UNKNOWN. It is up to the caller
+ * to set the action appropriately after the call.
+ *
+ * @param args the args from which the pointers should be removed
+ * @param pointerIds the pointer ids of the pointers that should be removed
+ */
 NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
                                   const std::set<int32_t>& pointerIds);
 
@@ -147,13 +166,21 @@
     std::vector<NotifyMotionArgs> processMotion(const NotifyMotionArgs& args);
 
     // Get the device info of this device, for comparison purposes
-    const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo();
+    const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo() const;
     std::string dump() const;
 
 private:
     PalmRejector(const PalmRejector&) = delete;
     PalmRejector& operator=(const PalmRejector&) = delete;
 
+    /**
+     * Update the slot state and send this event to the palm rejection model for palm detection.
+     * Return the pointer ids that should be suppressed.
+     *
+     * This function is not const because it has side-effects. It will update the slot state using
+     * the incoming args! Also, it will call Filter(..), which has side-effects.
+     */
+    std::set<int32_t> detectPalmPointers(const NotifyMotionArgs& args);
     std::unique_ptr<::ui::SharedPalmDetectionFilterState> mSharedPalmState;
     AndroidPalmFilterDeviceInfo mDeviceInfo;
     std::unique_ptr<::ui::PalmDetectionFilter> mPalmDetectionFilter;
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 75071d5..e5c19af 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -26,7 +26,6 @@
         "libinputreporter",
         "liblog",
         "libstatslog",
-        "libui",
         "libutils",
     ],
     static_libs: [
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 12eeea6..3d4dd7e 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -112,8 +112,6 @@
 
     void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
 
-    bool isPerDisplayTouchModeEnabled() override { return false; }
-
     InputDispatcherConfiguration mConfig;
 };
 
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index cdad9c9..eb79b76 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -63,7 +63,6 @@
         "libstatslog",
         "libstatspull",
         "libstatssocket",
-        "libui",
         "libgui",
         "libutils",
         "server_configurable_flags",
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
index 097dba5..5e5e0c4 100644
--- a/services/inputflinger/dispatcher/AnrTracker.h
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
-#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+#pragma once
 
 #include <binder/IBinder.h>
 #include <utils/Timers.h>
@@ -56,5 +55,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index fec5f83..d210e9e 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
-#define _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
+#pragma once
 
 #include <utils/BitSet.h>
 #include <optional>
@@ -55,5 +54,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index dc6a081..6040e9b 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
-#define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
+#pragma once
 
 #include "InputState.h"
 
@@ -74,5 +73,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index 4f8995f..d2ad407 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H
-#define _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H
+#pragma once
 
 #define LOG_TAG "InputDispatcher"
 
@@ -75,11 +74,8 @@
 
 /**
  * Log debug messages about touch occlusion
- * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires
- * restart)
  */
-const bool DEBUG_TOUCH_OCCLUSION =
-        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO);
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
 
 /**
  * Log debug messages about the app switch latency optimization.
@@ -95,5 +91,3 @@
 const bool DEBUG_HOVER =
         __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Hover", ANDROID_LOG_INFO);
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_DISPATCHER_DEBUG_CONFIG_H
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index d1c8b8a..9809148 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
-#define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
+#pragma once
 
 #include <gui/WindowInfo.h>
 #include <utils/StrongPointer.h>
@@ -44,5 +43,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 8046bbe..33e7e17 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -195,9 +195,10 @@
 
 // --- TouchModeEntry ---
 
-TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode)
+TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId)
       : EventEntry(id, Type::TOUCH_MODE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
-        inTouchMode(inTouchMode) {}
+        inTouchMode(inTouchMode),
+        displayId(displayId) {}
 
 TouchModeEntry::~TouchModeEntry() {}
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 0f79296..60f319a 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_ENTRY_H
-#define _UI_INPUT_INPUTDISPATCHER_ENTRY_H
+#pragma once
 
 #include "InjectionState.h"
 #include "InputTarget.h"
@@ -211,8 +210,9 @@
 
 struct TouchModeEntry : EventEntry {
     bool inTouchMode;
+    int32_t displayId;
 
-    TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode);
+    TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int32_t displayId);
     std::string getDescription() const override;
 
     ~TouchModeEntry() override;
@@ -257,5 +257,3 @@
                                                        const ui::Transform& rawTransform);
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 90cf150..d9e27ba 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
-#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
+#pragma once
 
 #include <stdint.h>
 #include "InputDispatcherInterface.h"
@@ -41,5 +40,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ff63967..a793f57 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -524,6 +524,21 @@
     return {};
 }
 
+Point resolveTouchedPosition(const MotionEntry& entry) {
+    const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
+    // Always dispatch mouse events to cursor position.
+    if (isFromMouse) {
+        return Point(static_cast<int32_t>(entry.xCursorPosition),
+                     static_cast<int32_t>(entry.yCursorPosition));
+    }
+
+    const int32_t pointerIndex = getMotionEventActionPointerIndex(entry.action);
+    return Point(static_cast<int32_t>(
+                         entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)),
+                 static_cast<int32_t>(
+                         entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)));
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -544,17 +559,12 @@
         mDispatchEnabled(false),
         mDispatchFrozen(false),
         mInputFilterEnabled(false),
-        // 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(kDefaultInTouchMode),
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
         mStaleEventTimeout(staleEventTimeout),
         mLatencyAggregator(),
-        mLatencyTracker(&mLatencyAggregator),
-        kPerDisplayTouchModeEnabled(mPolicy->isPerDisplayTouchModeEnabled()) {
+        mLatencyTracker(&mLatencyAggregator) {
     mLooper = sp<Looper>::make(false);
     mReporter = createInputReporter();
 
@@ -562,7 +572,6 @@
     SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
 
     mKeyRepeatState.lastKeyEntry = nullptr;
-
     policy->getDispatcherConfiguration(&mConfig);
 }
 
@@ -927,13 +936,10 @@
     // If the application takes too long to catch up then we drop all events preceding
     // the touch into the other window.
     if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
-        int32_t displayId = motionEntry.displayId;
-        int32_t x = static_cast<int32_t>(
-                motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-        int32_t y = static_cast<int32_t>(
-                motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-
+        const int32_t displayId = motionEntry.displayId;
+        const auto [x, y] = resolveTouchedPosition(motionEntry);
         const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
+
         sp<WindowInfoHandle> touchedWindowHandle =
                 findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus);
         if (touchedWindowHandle != nullptr &&
@@ -1435,7 +1441,7 @@
 void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime,
                                                     const std::shared_ptr<TouchModeEntry>& entry) {
     const std::vector<sp<WindowInfoHandle>>& windowHandles =
-            getWindowHandlesLocked(mFocusedDisplayId);
+            getWindowHandlesLocked(entry->displayId);
     if (windowHandles.empty()) {
         return;
     }
@@ -1452,7 +1458,6 @@
         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;
@@ -2074,18 +2079,8 @@
 
     if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
         /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
-
-        int32_t x;
-        int32_t y;
+        const auto [x, y] = resolveTouchedPosition(entry);
         const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-        // Always dispatch mouse events to cursor position.
-        if (isFromMouse) {
-            x = int32_t(entry.xCursorPosition);
-            y = int32_t(entry.yCursorPosition);
-        } else {
-            x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
-            y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
-        }
         const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         const bool isStylus = isPointerFromStylus(entry, pointerIndex);
         newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
@@ -2148,43 +2143,7 @@
         }
 
         for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
-            const WindowInfo& info = *windowHandle->getInfo();
-
-            // Skip spy window targets that are not valid for targeted injection.
-            if (const auto err = verifyTargetedInjection(windowHandle, entry); err) {
-                continue;
-            }
-
-            if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
-                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
-            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());
-                    }
-                }
-                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)) {
+            if (!canWindowReceiveMotionLocked(windowHandle, entry)) {
                 continue;
             }
 
@@ -2240,9 +2199,7 @@
         // Check whether touches should slip outside of the current foreground window.
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
             tempTouchState.isSlippery()) {
-            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 auto [x, y] = resolveTouchedPosition(entry);
             const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
             sp<WindowInfoHandle> oldTouchedWindowHandle =
                     tempTouchState.getFirstForegroundWindowHandle();
@@ -2991,7 +2948,7 @@
         ATRACE_NAME(message.c_str());
     }
 
-    bool wasEmpty = connection->outboundQueue.empty();
+    const bool wasEmpty = connection->outboundQueue.empty();
 
     // Enqueue dispatch entries for the requested modes.
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
@@ -3674,6 +3631,8 @@
     target.inputChannel = connection->inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
+    const bool wasEmpty = connection->outboundQueue.empty();
+
     for (size_t i = 0; i < cancelationEvents.size(); i++) {
         std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
         switch (cancelationEventEntry->type) {
@@ -3708,7 +3667,10 @@
                                    InputTarget::FLAG_DISPATCH_AS_IS);
     }
 
-    startDispatchCycleLocked(currentTime, connection);
+    // If the outbound queue was previously empty, start the dispatch cycle going.
+    if (wasEmpty && !connection->outboundQueue.empty()) {
+        startDispatchCycleLocked(currentTime, connection);
+    }
 }
 
 void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
@@ -3740,6 +3702,7 @@
     target.inputChannel = connection->inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
+    const bool wasEmpty = connection->outboundQueue.empty();
     for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
         switch (downEventEntry->type) {
             case EventEntry::Type::MOTION: {
@@ -3766,7 +3729,10 @@
                                    InputTarget::FLAG_DISPATCH_AS_IS);
     }
 
-    startDispatchCycleLocked(downTime, connection);
+    // If the outbound queue was previously empty, start the dispatch cycle going.
+    if (wasEmpty && !connection->outboundQueue.empty()) {
+        startDispatchCycleLocked(downTime, connection);
+    }
 }
 
 std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
@@ -4605,26 +4571,58 @@
     return getWindowHandleLocked(focusedToken, displayId);
 }
 
-bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
-    sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
-    const bool noInputChannel =
-            windowHandle.getInfo()->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
-    if (connection != nullptr && noInputChannel) {
-        ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s",
-              windowHandle.getName().c_str(), connection->inputChannel->getName().c_str());
+bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window,
+                                                   const MotionEntry& motionEntry) const {
+    const WindowInfo& info = *window->getInfo();
+
+    // Skip spy window targets that are not valid for targeted injection.
+    if (const auto err = verifyTargetedInjection(window, motionEntry); err) {
         return false;
     }
 
+    if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
+        ALOGI("Not sending touch event to %s because it is paused", window->getName().c_str());
+        return false;
+    }
+
+    if (info.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
+        ALOGW("Not sending touch gesture to %s because it has config NO_INPUT_CHANNEL",
+              window->getName().c_str());
+        return false;
+    }
+
+    sp<Connection> connection = getConnectionLocked(window->getToken());
     if (connection == nullptr) {
-        if (!noInputChannel) {
-            ALOGI("Could not find connection for %s", windowHandle.getName().c_str());
-        }
+        ALOGW("Not sending touch to %s because there's no corresponding connection",
+              window->getName().c_str());
         return false;
     }
+
     if (!connection->responsive) {
-        ALOGW("Window %s is not responsive", windowHandle.getName().c_str());
+        ALOGW("Not sending touch to %s because it is not responsive", window->getName().c_str());
         return false;
     }
+
+    // Drop events that can't be trusted due to occlusion
+    const auto [x, y] = resolveTouchedPosition(motionEntry);
+    TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(window, 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());
+            }
+        }
+        ALOGW("Dropping untrusted touch event due to %s/%d", occlusionInfo.obscuringPackage.c_str(),
+              occlusionInfo.obscuringUid);
+        return false;
+    }
+
+    // Drop touch events if requested by input feature
+    if (shouldDropInput(motionEntry, window)) {
+        return false;
+    }
+
     return true;
 }
 
@@ -5003,15 +5001,20 @@
     bool needWake = false;
     {
         std::scoped_lock lock(mLock);
-        if (mInTouchMode == inTouchMode) {
+        ALOGD_IF(DEBUG_TOUCH_MODE,
+                 "Request to change touch mode to %s (calling pid=%d, uid=%d, "
+                 "hasPermission=%s, target displayId=%d, mTouchModePerDisplay[displayId]=%s)",
+                 toString(inTouchMode), pid, uid, toString(hasPermission), displayId,
+                 mTouchModePerDisplay.count(displayId) == 0
+                         ? "not set"
+                         : std::to_string(mTouchModePerDisplay[displayId]).c_str());
+
+        // TODO(b/198499018): Ensure that WM can guarantee that touch mode is properly set when
+        // display is created.
+        auto touchModeIt = mTouchModePerDisplay.find(displayId);
+        if (touchModeIt != mTouchModePerDisplay.end() && touchModeIt->second == inTouchMode) {
             return false;
         }
-        if (DEBUG_TOUCH_MODE) {
-            ALOGD("Request to change touch mode from %s to %s (calling pid=%d, uid=%d, "
-                  "hasPermission=%s, target displayId=%d, perDisplayTouchModeEnabled=%s)",
-                  toString(mInTouchMode), toString(inTouchMode), pid, uid, toString(hasPermission),
-                  displayId, toString(kPerDisplayTouchModeEnabled));
-        }
         if (!hasPermission) {
             if (!focusedWindowIsOwnedByLocked(pid, uid) &&
                 !recentWindowsAreOwnedByLocked(pid, uid)) {
@@ -5021,11 +5024,9 @@
                 return false;
             }
         }
-
-        // TODO(b/198499018): Store touch mode per display (kPerDisplayTouchModeEnabled)
-        mInTouchMode = inTouchMode;
-
-        auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode);
+        mTouchModePerDisplay[displayId] = inTouchMode;
+        auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode,
+                                                      displayId);
         needWake = enqueueInboundEventLocked(std::move(entry));
     } // release lock
 
@@ -5465,6 +5466,16 @@
         dump += INDENT "AppSwitch: not pending\n";
     }
 
+    if (!mTouchModePerDisplay.empty()) {
+        dump += INDENT "TouchModePerDisplay:\n";
+        for (const auto& [displayId, touchMode] : mTouchModePerDisplay) {
+            dump += StringPrintf(INDENT2 "Display: %" PRId32 " TouchMode: %s\n", displayId,
+                                 std::to_string(touchMode).c_str());
+        }
+    } else {
+        dump += INDENT "TouchModePerDisplay: <none>\n";
+    }
+
     dump += INDENT "Configuration:\n";
     dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay));
     dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
@@ -6357,6 +6368,8 @@
         mFocusResolver.displayRemoved(displayId);
         // Reset pointer capture eligibility, regardless of previous state.
         std::erase(mIneligibleDisplaysForPointerCapture, displayId);
+        // Remove the associated touch mode state.
+        mTouchModePerDisplay.erase(displayId);
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index be619ae..14b046a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_DISPATCHER_H
-#define _UI_INPUT_DISPATCHER_H
+#pragma once
 
 #include "AnrTracker.h"
 #include "CancelationOptions.h"
@@ -42,7 +41,6 @@
 #include <input/InputTransport.h>
 #include <limits.h>
 #include <stddef.h>
-#include <ui/Region.h>
 #include <unistd.h>
 #include <utils/BitSet.h>
 #include <utils/Looper.h>
@@ -344,9 +342,13 @@
     bool mDispatchEnabled GUARDED_BY(mLock);
     bool mDispatchFrozen GUARDED_BY(mLock);
     bool mInputFilterEnabled GUARDED_BY(mLock);
-    bool mInTouchMode GUARDED_BY(mLock);
     float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
 
+    // This map is not really needed, but it helps a lot with debugging (dumpsys input).
+    // In the java layer, touch mode states are spread across multiple DisplayContent objects,
+    // making harder to snapshot and retrieve them.
+    std::map<int32_t /*displayId*/, bool /*inTouchMode*/> mTouchModePerDisplay GUARDED_BY(mLock);
+
     class DispatcherWindowListener : public gui::WindowInfosListener {
     public:
         explicit DispatcherWindowListener(InputDispatcher& dispatcher) : mDispatcher(dispatcher){};
@@ -382,10 +384,10 @@
             REQUIRES(mLock);
     sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const
             REQUIRES(mLock);
-    bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const
-            REQUIRES(mLock);
+    bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window,
+                                      const MotionEntry& motionEntry) const REQUIRES(mLock);
 
-    // Gets all the input targets (with their respective input channels) from the window handles
+    // Returns 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
@@ -686,12 +688,7 @@
     bool focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
     bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
 
-    // Per display touch mode enabled
-    const bool kPerDisplayTouchModeEnabled;
-
     sp<InputReporterInterface> mReporter;
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_DISPATCHER_H
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
index 77b8472..daf375d 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.h
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
+#pragma once
 
 #include <binder/IBinder.h>
 #include <input/Input.h>
@@ -104,5 +103,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 77a6db1..6ab48c9 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
+#pragma once
 
 #include "CancelationOptions.h"
 #include "Entry.h"
@@ -134,5 +133,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index ac20dab..b2966f6 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
+#pragma once
 
 #include <gui/constants.h>
 #include <input/InputTransport.h>
@@ -136,5 +135,3 @@
 std::string dispatchModeToString(int32_t dispatchMode);
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h
index ed5731f..accfc29 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.h
+++ b/services/inputflinger/dispatcher/LatencyAggregator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
-#define _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
+#pragma once
 
 #include <kll.h>
 #include <statslog.h>
@@ -86,5 +85,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 4b0c618..64dfeef 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
-#define _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
+#pragma once
 
 #include <map>
 #include <unordered_map>
@@ -81,5 +80,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index 365d5be..7b51191 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_MONITOR_H
-#define _UI_INPUT_INPUTDISPATCHER_MONITOR_H
+#pragma once
 
 #include <input/InputTransport.h>
 
@@ -30,5 +29,3 @@
 };
 
 } // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 1fb51e1..cf5f1e5 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
-#define _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
+#pragma once
 
 #include "Monitor.h"
 #include "TouchedWindow.h"
@@ -67,5 +66,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index a7839db..a6c505b 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
-#define _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
+#pragma once
 
 namespace android {
 
@@ -38,5 +37,3 @@
 
 } // namespace inputdispatcher
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
index 00abf47..5eb3a32 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
+#pragma once
 
 #include <utils/Timers.h>
 
@@ -40,5 +39,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
index 38d0c32..5247d8e 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
+#pragma once
 
 #include <utils/StrongPointer.h>
 
@@ -29,5 +28,3 @@
         const sp<InputDispatcherPolicyInterface>& policy);
 
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 32b3ddb..484b0d3 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
+#pragma once
 
 #include <InputListener.h>
 #include <android-base/result.h>
@@ -226,5 +225,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 7c299b2..7843923 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
+#pragma once
 
 #include "InputDispatcherConfiguration.h"
 
@@ -137,11 +136,6 @@
 
     /* Notifies the policy that the drag window has moved over to another window */
     virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
-
-    /* If touch mode is enabled per display or global */
-    virtual bool isPerDisplayTouchModeEnabled() = 0;
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H
diff --git a/services/inputflinger/host/InputDriver.h b/services/inputflinger/host/InputDriver.h
index e56673b..b4c9094 100644
--- a/services/inputflinger/host/InputDriver.h
+++ b/services/inputflinger/host/InputDriver.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_INPUT_DRIVER_H
-#define ANDROID_INPUT_DRIVER_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -192,4 +191,3 @@
 }
 
 } // namespace android
-#endif // ANDROID_INPUT_DRIVER_H
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 3cf1b2b..388988b 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_INPUT_FLINGER_H
-#define ANDROID_INPUT_FLINGER_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -58,5 +57,3 @@
 };
 
 } // namespace android
-
-#endif // ANDROID_INPUT_FLINGER_H
diff --git a/services/inputflinger/host/InputHost.h b/services/inputflinger/host/InputHost.h
index eda4a89..bdc4225 100644
--- a/services/inputflinger/host/InputHost.h
+++ b/services/inputflinger/host/InputHost.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_INPUT_HOST_H
-#define ANDROID_INPUT_HOST_H
+#pragma once
 
 #include <vector>
 
@@ -55,4 +54,3 @@
 };
 
 } // namespace android
-#endif // ANDRIOD_INPUT_HOST_H
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index d9822ce..1bb1968 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -14,237 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_LISTENER_H
-#define _UI_INPUT_LISTENER_H
+#pragma once
 
 #include <vector>
 
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/TouchVideoFrame.h>
+#include "NotifyArgs.h"
 
 namespace android {
 
-class InputListenerInterface;
-
-
-/* Superclass of all input event argument objects */
-struct NotifyArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    inline NotifyArgs() : id(0), eventTime(0) {}
-
-    inline explicit NotifyArgs(int32_t id, nsecs_t eventTime) : id(id), eventTime(eventTime) {}
-
-    virtual ~NotifyArgs() { }
-
-    virtual void notify(InputListenerInterface& listener) const = 0;
-};
-
-
-/* Describes a configuration change event. */
-struct NotifyConfigurationChangedArgs : public NotifyArgs {
-
-    inline NotifyConfigurationChangedArgs() { }
-
-    bool operator==(const NotifyConfigurationChangedArgs& rhs) const;
-
-    NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
-
-    NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
-
-    virtual ~NotifyConfigurationChangedArgs() { }
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-
-/* Describes a key event. */
-struct NotifyKeyArgs : public NotifyArgs {
-    int32_t deviceId;
-    uint32_t source;
-    int32_t displayId;
-    uint32_t policyFlags;
-    int32_t action;
-    int32_t flags;
-    int32_t keyCode;
-    int32_t scanCode;
-    int32_t metaState;
-    nsecs_t downTime;
-    nsecs_t readTime;
-
-    inline NotifyKeyArgs() { }
-
-    NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
-                  uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
-                  int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
-                  nsecs_t downTime);
-
-    bool operator==(const NotifyKeyArgs& rhs) const;
-
-    NotifyKeyArgs(const NotifyKeyArgs& other);
-
-    virtual ~NotifyKeyArgs() { }
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-
-/* Describes a motion event. */
-struct NotifyMotionArgs : public NotifyArgs {
-    int32_t deviceId;
-    uint32_t source;
-    int32_t displayId;
-    uint32_t policyFlags;
-    int32_t action;
-    int32_t actionButton;
-    int32_t flags;
-    int32_t metaState;
-    int32_t buttonState;
-    /**
-     * Classification of the current touch gesture
-     */
-    MotionClassification classification;
-    int32_t edgeFlags;
-
-    uint32_t pointerCount;
-    PointerProperties pointerProperties[MAX_POINTERS];
-    PointerCoords pointerCoords[MAX_POINTERS];
-    float xPrecision;
-    float yPrecision;
-    /**
-     * Mouse cursor position when this event is reported relative to the origin of the specified
-     * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in
-     * gestures enabled mode.
-     */
-    float xCursorPosition;
-    float yCursorPosition;
-    nsecs_t downTime;
-    nsecs_t readTime;
-    std::vector<TouchVideoFrame> videoFrames;
-
-    inline NotifyMotionArgs() { }
-
-    NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
-                     uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
-                     int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
-                     MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
-                     const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-                     float xPrecision, float yPrecision, float xCursorPosition,
-                     float yCursorPosition, nsecs_t downTime,
-                     const std::vector<TouchVideoFrame>& videoFrames);
-
-    NotifyMotionArgs(const NotifyMotionArgs& other);
-
-    virtual ~NotifyMotionArgs() { }
-
-    bool operator==(const NotifyMotionArgs& rhs) const;
-
-    void notify(InputListenerInterface& listener) const override;
-
-    std::string dump() const;
-};
-
-/* Describes a sensor event. */
-struct NotifySensorArgs : public NotifyArgs {
-    int32_t deviceId;
-    uint32_t source;
-    InputDeviceSensorType sensorType;
-    InputDeviceSensorAccuracy accuracy;
-    bool accuracyChanged;
-    nsecs_t hwTimestamp;
-    std::vector<float> values;
-
-    inline NotifySensorArgs() {}
-
-    NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                     InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
-                     bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
-
-    NotifySensorArgs(const NotifySensorArgs& other);
-
-    bool operator==(const NotifySensorArgs rhs) const;
-
-    ~NotifySensorArgs() override {}
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-/* Describes a switch event. */
-struct NotifySwitchArgs : public NotifyArgs {
-    uint32_t policyFlags;
-    uint32_t switchValues;
-    uint32_t switchMask;
-
-    inline NotifySwitchArgs() { }
-
-    NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues,
-                     uint32_t switchMask);
-
-    NotifySwitchArgs(const NotifySwitchArgs& other);
-
-    bool operator==(const NotifySwitchArgs rhs) const;
-
-    virtual ~NotifySwitchArgs() { }
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-
-/* Describes a device reset event, such as when a device is added,
- * reconfigured, or removed. */
-struct NotifyDeviceResetArgs : public NotifyArgs {
-    int32_t deviceId;
-
-    inline NotifyDeviceResetArgs() { }
-
-    NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId);
-
-    NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
-
-    bool operator==(const NotifyDeviceResetArgs& rhs) const;
-
-    virtual ~NotifyDeviceResetArgs() { }
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-/* Describes a change in the state of Pointer Capture. */
-struct NotifyPointerCaptureChangedArgs : public NotifyArgs {
-    // The sequence number of the Pointer Capture request, if enabled.
-    PointerCaptureRequest request;
-
-    inline NotifyPointerCaptureChangedArgs() {}
-
-    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
-
-    NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other);
-
-    bool operator==(const NotifyPointerCaptureChangedArgs& rhs) const;
-
-    virtual ~NotifyPointerCaptureChangedArgs() {}
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-/* Describes a vibrator state event. */
-struct NotifyVibratorStateArgs : public NotifyArgs {
-    int32_t deviceId;
-    bool isOn;
-
-    inline NotifyVibratorStateArgs() {}
-
-    NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn);
-
-    NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other);
-
-    bool operator==(const NotifyVibratorStateArgs rhs) const;
-
-    virtual ~NotifyVibratorStateArgs() {}
-
-    void notify(InputListenerInterface& listener) const override;
-};
+std::list<NotifyArgs>& operator+=(std::list<NotifyArgs>& keep, std::list<NotifyArgs>&& consume);
 
 /*
  * The interface used by the InputReader to notify the InputListener about input events.
@@ -264,6 +45,8 @@
     virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) = 0;
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
     virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0;
+
+    void notify(const NotifyArgs& args);
 };
 
 /*
@@ -288,9 +71,7 @@
 
 private:
     InputListenerInterface& mInnerListener;
-    std::vector<std::unique_ptr<NotifyArgs>> mArgsQueue;
+    std::vector<NotifyArgs> mArgsQueue;
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_LISTENER_H
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 77c9142..cacb63c 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_READER_BASE_H
-#define _UI_INPUT_READER_BASE_H
+#pragma once
 
 #include <android/os/IInputConstants.h>
 #include <input/DisplayViewport.h>
@@ -114,6 +113,8 @@
     virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0;
     /* Get battery status of a particular input device. */
     virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
+    /* Get the device path for the battery of an input device. */
+    virtual std::optional<std::string> getBatteryDevicePath(int32_t deviceId) = 0;
 
     virtual std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) = 0;
 
@@ -395,5 +396,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_READER_COMMON_H
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
index 407365a..5e75027 100644
--- a/services/inputflinger/include/InputThread.h
+++ b/services/inputflinger/include/InputThread.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_THREAD_H
-#define _UI_INPUT_THREAD_H
+#pragma once
 
 #include <utils/Thread.h>
 
@@ -42,5 +41,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUT_THREAD_H
\ No newline at end of file
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
new file mode 100644
index 0000000..f28dbf3
--- /dev/null
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/TouchVideoFrame.h>
+
+namespace android {
+
+/* Describes a configuration change event. */
+struct NotifyConfigurationChangedArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    inline NotifyConfigurationChangedArgs() {}
+
+    NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
+
+    bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default;
+
+    NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default;
+};
+
+/* Describes a key event. */
+struct NotifyKeyArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    uint32_t source;
+    int32_t displayId;
+    uint32_t policyFlags;
+    int32_t action;
+    int32_t flags;
+    int32_t keyCode;
+    int32_t scanCode;
+    int32_t metaState;
+    nsecs_t downTime;
+    nsecs_t readTime;
+
+    inline NotifyKeyArgs() {}
+
+    NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                  uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+                  int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+                  nsecs_t downTime);
+
+    bool operator==(const NotifyKeyArgs& rhs) const = default;
+
+    NotifyKeyArgs(const NotifyKeyArgs& other) = default;
+};
+
+/* Describes a motion event. */
+struct NotifyMotionArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    uint32_t source;
+    int32_t displayId;
+    uint32_t policyFlags;
+    int32_t action;
+    int32_t actionButton;
+    int32_t flags;
+    int32_t metaState;
+    int32_t buttonState;
+    /**
+     * Classification of the current touch gesture
+     */
+    MotionClassification classification;
+    int32_t edgeFlags;
+
+    uint32_t pointerCount;
+    PointerProperties pointerProperties[MAX_POINTERS];
+    PointerCoords pointerCoords[MAX_POINTERS];
+    float xPrecision;
+    float yPrecision;
+    /**
+     * Mouse cursor position when this event is reported relative to the origin of the specified
+     * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in
+     * gestures enabled mode.
+     */
+    float xCursorPosition;
+    float yCursorPosition;
+    nsecs_t downTime;
+    nsecs_t readTime;
+    std::vector<TouchVideoFrame> videoFrames;
+
+    inline NotifyMotionArgs() {}
+
+    NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                     uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+                     int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+                     MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
+                     const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
+                     float xPrecision, float yPrecision, float xCursorPosition,
+                     float yCursorPosition, nsecs_t downTime,
+                     const std::vector<TouchVideoFrame>& videoFrames);
+
+    NotifyMotionArgs(const NotifyMotionArgs& other);
+
+    bool operator==(const NotifyMotionArgs& rhs) const;
+
+    std::string dump() const;
+};
+
+/* Describes a sensor event. */
+struct NotifySensorArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    uint32_t source;
+    InputDeviceSensorType sensorType;
+    InputDeviceSensorAccuracy accuracy;
+    bool accuracyChanged;
+    nsecs_t hwTimestamp;
+    std::vector<float> values;
+
+    inline NotifySensorArgs() {}
+
+    NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                     InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
+                     bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
+
+    NotifySensorArgs(const NotifySensorArgs& other) = default;
+};
+
+/* Describes a switch event. */
+struct NotifySwitchArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    uint32_t policyFlags;
+    uint32_t switchValues;
+    uint32_t switchMask;
+
+    inline NotifySwitchArgs() {}
+
+    NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues,
+                     uint32_t switchMask);
+
+    NotifySwitchArgs(const NotifySwitchArgs& other) = default;
+
+    bool operator==(const NotifySwitchArgs& rhs) const = default;
+};
+
+/* Describes a device reset event, such as when a device is added,
+ * reconfigured, or removed. */
+struct NotifyDeviceResetArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+
+    inline NotifyDeviceResetArgs() {}
+
+    NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId);
+
+    NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) = default;
+
+    bool operator==(const NotifyDeviceResetArgs& rhs) const = default;
+};
+
+/* Describes a change in the state of Pointer Capture. */
+struct NotifyPointerCaptureChangedArgs {
+    // The sequence number of the Pointer Capture request, if enabled.
+    int32_t id;
+    nsecs_t eventTime;
+
+    PointerCaptureRequest request;
+
+    inline NotifyPointerCaptureChangedArgs() {}
+
+    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
+
+    NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other) = default;
+};
+
+/* Describes a vibrator state event. */
+struct NotifyVibratorStateArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    bool isOn;
+
+    inline NotifyVibratorStateArgs() {}
+
+    NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn);
+
+    NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other) = default;
+};
+
+using NotifyArgs = std::variant<NotifyConfigurationChangedArgs, NotifyKeyArgs, NotifyMotionArgs,
+                                NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
+                                NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>;
+
+const char* toString(const NotifyArgs& args);
+
+} // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index db4228d..647e10c 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
-#define _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
+#pragma once
 
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
@@ -108,5 +107,3 @@
 };
 
 } // namespace android
-
-#endif // _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h
index 736041e..04c2e39 100644
--- a/services/inputflinger/include/VibrationElement.h
+++ b/services/inputflinger/include/VibrationElement.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _VIBRATION_ELEMENT_H
-#define _VIBRATION_ELEMENT_H
+#pragma once
 
 #include <array>
 #include <chrono>
@@ -71,5 +70,3 @@
 };
 
 } // namespace android
-
-#endif // _VIBRATION_ELEMENT_H
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 01146a3..0f87201 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -69,12 +69,12 @@
         "libinput",
         "liblog",
         "libstatslog",
-        "libui",
         "libutils",
         "libPlatformProperties",
     ],
     static_libs: [
         "libc++fs",
+        "libui-types",
     ],
     header_libs: [
         "libbatteryservice_headers",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index bfa44ac..ca7e426 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -52,6 +52,7 @@
 
 #include <filesystem>
 #include <regex>
+#include <utility>
 
 #include "EventHub.h"
 
@@ -60,6 +61,7 @@
 #define INDENT3 "      "
 
 using android::base::StringPrintf;
+using android::hardware::input::InputDeviceCountryCode;
 
 namespace android {
 
@@ -74,6 +76,8 @@
 static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
 static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
 
+static constexpr size_t EVENT_BUFFER_SIZE = 256;
+
 // Mapping for input battery class node IDs lookup.
 // https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
 static const std::unordered_map<std::string, InputBatteryClass> BATTERY_CLASSES =
@@ -115,7 +119,8 @@
          {"brightness", InputLightClass::BRIGHTNESS},
          {"multi_index", InputLightClass::MULTI_INDEX},
          {"multi_intensity", InputLightClass::MULTI_INTENSITY},
-         {"max_brightness", InputLightClass::MAX_BRIGHTNESS}};
+         {"max_brightness", InputLightClass::MAX_BRIGHTNESS},
+         {"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT}};
 
 // Mapping for input multicolor led class node names.
 // https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html
@@ -193,8 +198,7 @@
 }
 
 /**
- * Returns the sysfs root path of the input device
- *
+ * Returns the sysfs root path of the input device.
  */
 static std::optional<std::filesystem::path> getSysfsRootPath(const char* devicePath) {
     std::error_code errorCode;
@@ -301,6 +305,112 @@
     return colors;
 }
 
+/**
+ * Read country code information exposed through the sysfs path.
+ */
+static InputDeviceCountryCode readCountryCodeLocked(const std::filesystem::path& sysfsRootPath) {
+    // Check the sysfs root path
+    int hidCountryCode = static_cast<int>(InputDeviceCountryCode::INVALID);
+    std::string str;
+    if (base::ReadFileToString(sysfsRootPath / "country", &str)) {
+        hidCountryCode = std::stoi(str, nullptr, 16);
+        LOG_ALWAYS_FATAL_IF(hidCountryCode > 35 || hidCountryCode < 0,
+                            "HID country code should be in range [0, 35]. Found country code "
+                            "to be %d",
+                            hidCountryCode);
+    }
+
+    return static_cast<InputDeviceCountryCode>(hidCountryCode);
+}
+
+/**
+ * Read information about batteries exposed through the sysfs path.
+ */
+static std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> readBatteryConfiguration(
+        const std::filesystem::path& sysfsRootPath) {
+    std::unordered_map<int32_t, RawBatteryInfo> batteryInfos;
+    int32_t nextBatteryId = 0;
+    // Check if device has any battery.
+    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY);
+    for (const auto& nodePath : paths) {
+        RawBatteryInfo info;
+        info.id = ++nextBatteryId;
+        info.path = nodePath;
+        info.name = nodePath.filename();
+
+        // Scan the path for all the files
+        // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
+        const auto& files = allFilesInPath(nodePath);
+        for (const auto& file : files) {
+            const auto it = BATTERY_CLASSES.find(file.filename().string());
+            if (it != BATTERY_CLASSES.end()) {
+                info.flags |= it->second;
+            }
+        }
+        batteryInfos.insert_or_assign(info.id, info);
+        ALOGD("configureBatteryLocked rawBatteryId %d name %s", info.id, info.name.c_str());
+    }
+    return batteryInfos;
+}
+
+/**
+ *  Read information about lights exposed through the sysfs path.
+ */
+static std::unordered_map<int32_t /*lightId*/, RawLightInfo> readLightsConfiguration(
+        const std::filesystem::path& sysfsRootPath) {
+    std::unordered_map<int32_t, RawLightInfo> lightInfos;
+    int32_t nextLightId = 0;
+    // Check if device has any lights.
+    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
+    for (const auto& nodePath : paths) {
+        RawLightInfo info;
+        info.id = ++nextLightId;
+        info.path = nodePath;
+        info.name = nodePath.filename();
+        info.maxBrightness = std::nullopt;
+
+        // Light name should follow the naming pattern <name>:<color>:<function>
+        // Refer kernel docs /leds/leds-class.html for valid supported LED names.
+        std::regex indexPattern("([a-zA-Z0-9_.:]*:)?([a-zA-Z0-9_.]*):([a-zA-Z0-9_.]*)");
+        std::smatch results;
+
+        if (std::regex_match(info.name, results, indexPattern)) {
+            // regex_match will return full match at index 0 and <name> at index 1. For RawLightInfo
+            // we only care about sections <color> and <function> which will be at index 2 and 3.
+            for (int i = 2; i <= 3; i++) {
+                const auto it = LIGHT_CLASSES.find(results.str(i));
+                if (it != LIGHT_CLASSES.end()) {
+                    info.flags |= it->second;
+                }
+            }
+
+            // Set name of the raw light to <function> which represents playerIDs for LEDs that
+            // turn on/off based on the current player ID (Refer to PeripheralController.cpp for
+            // player ID logic)
+            info.name = results.str(3);
+        }
+        // Scan the path for all the files
+        // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
+        const auto& files = allFilesInPath(nodePath);
+        for (const auto& file : files) {
+            const auto it = LIGHT_CLASSES.find(file.filename().string());
+            if (it != LIGHT_CLASSES.end()) {
+                info.flags |= it->second;
+                // If the node has maximum brightness, read it
+                if (it->second == InputLightClass::MAX_BRIGHTNESS) {
+                    std::string str;
+                    if (base::ReadFileToString(file, &str)) {
+                        info.maxBrightness = std::stoi(str);
+                    }
+                }
+            }
+        }
+        lightInfos.insert_or_assign(info.id, info);
+        ALOGD("configureLightsLocked rawLightId %d name %s", info.id, info.name.c_str());
+    }
+    return lightInfos;
+}
+
 // --- Global Functions ---
 
 ftl::Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis,
@@ -357,18 +467,18 @@
 
 // --- EventHub::Device ---
 
-EventHub::Device::Device(int fd, int32_t id, const std::string& path,
-                         const InputDeviceIdentifier& identifier)
+EventHub::Device::Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier,
+                         std::shared_ptr<const AssociatedDevice> assocDev)
       : fd(fd),
         id(id),
-        path(path),
-        identifier(identifier),
+        path(std::move(path)),
+        identifier(std::move(identifier)),
         classes(0),
         configuration(nullptr),
         virtualKeyMap(nullptr),
         ffEffectPlaying(false),
         ffEffectId(-1),
-        associatedDevice(nullptr),
+        associatedDevice(std::move(assocDev)),
         controllerNumber(0),
         enabled(true),
         isVirtual(fd < 0) {}
@@ -557,75 +667,6 @@
     return NAME_NOT_FOUND;
 }
 
-// Check the sysfs path for any input device batteries, returns true if battery found.
-bool EventHub::AssociatedDevice::configureBatteryLocked() {
-    nextBatteryId = 0;
-    // Check if device has any battery.
-    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY);
-    for (const auto& nodePath : paths) {
-        RawBatteryInfo info;
-        info.id = ++nextBatteryId;
-        info.path = nodePath;
-        info.name = nodePath.filename();
-
-        // Scan the path for all the files
-        // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
-        const auto& files = allFilesInPath(nodePath);
-        for (const auto& file : files) {
-            const auto it = BATTERY_CLASSES.find(file.filename().string());
-            if (it != BATTERY_CLASSES.end()) {
-                info.flags |= it->second;
-            }
-        }
-        batteryInfos.insert_or_assign(info.id, info);
-        ALOGD("configureBatteryLocked rawBatteryId %d name %s", info.id, info.name.c_str());
-    }
-    return !batteryInfos.empty();
-}
-
-// Check the sysfs path for any input device lights, returns true if lights found.
-bool EventHub::AssociatedDevice::configureLightsLocked() {
-    nextLightId = 0;
-    // Check if device has any lights.
-    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
-    for (const auto& nodePath : paths) {
-        RawLightInfo info;
-        info.id = ++nextLightId;
-        info.path = nodePath;
-        info.name = nodePath.filename();
-        info.maxBrightness = std::nullopt;
-        size_t nameStart = info.name.rfind(":");
-        if (nameStart != std::string::npos) {
-            // Trim the name to color name
-            info.name = info.name.substr(nameStart + 1);
-            // Set InputLightClass flag for colors
-            const auto it = LIGHT_CLASSES.find(info.name);
-            if (it != LIGHT_CLASSES.end()) {
-                info.flags |= it->second;
-            }
-        }
-        // Scan the path for all the files
-        // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
-        const auto& files = allFilesInPath(nodePath);
-        for (const auto& file : files) {
-            const auto it = LIGHT_CLASSES.find(file.filename().string());
-            if (it != LIGHT_CLASSES.end()) {
-                info.flags |= it->second;
-                // If the node has maximum brightness, read it
-                if (it->second == InputLightClass::MAX_BRIGHTNESS) {
-                    std::string str;
-                    if (base::ReadFileToString(file, &str)) {
-                        info.maxBrightness = std::stoi(str);
-                    }
-                }
-            }
-        }
-        lightInfos.insert_or_assign(info.id, info);
-        ALOGD("configureLightsLocked rawLightId %d name %s", info.id, info.name.c_str());
-    }
-    return !lightInfos.empty();
-}
-
 /**
  * Get the capabilities for the current process.
  * Crashes the system if unable to create / check / destroy the capabilities object.
@@ -1034,7 +1075,7 @@
 }
 
 base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId,
-                                                                            int32_t absCode) {
+                                                                            int32_t absCode) const {
     std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
 
@@ -1056,18 +1097,19 @@
     return device->associatedDevice->batteryInfos;
 }
 
-const std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) {
+std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) const {
     std::scoped_lock _l(mLock);
     std::vector<int32_t> batteryIds;
 
-    for (const auto [id, info] : getBatteryInfoLocked(deviceId)) {
+    for (const auto& [id, info] : getBatteryInfoLocked(deviceId)) {
         batteryIds.push_back(id);
     }
 
     return batteryIds;
 }
 
-std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId,
+                                                          int32_t batteryId) const {
     std::scoped_lock _l(mLock);
 
     const auto infos = getBatteryInfoLocked(deviceId);
@@ -1081,7 +1123,7 @@
 }
 
 // Gets the light info map from light ID to RawLightInfo of the miscellaneous device associated
-// with the deivice ID. Returns an empty map if no miscellaneous device found.
+// with the device ID. Returns an empty map if no miscellaneous device found.
 const std::unordered_map<int32_t, RawLightInfo>& EventHub::getLightInfoLocked(
         int32_t deviceId) const {
     static const std::unordered_map<int32_t, RawLightInfo> EMPTY_LIGHT_INFO = {};
@@ -1092,18 +1134,18 @@
     return device->associatedDevice->lightInfos;
 }
 
-const std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) {
+std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) const {
     std::scoped_lock _l(mLock);
     std::vector<int32_t> lightIds;
 
-    for (const auto [id, info] : getLightInfoLocked(deviceId)) {
+    for (const auto& [id, info] : getLightInfoLocked(deviceId)) {
         lightIds.push_back(id);
     }
 
     return lightIds;
 }
 
-std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) {
+std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) const {
     std::scoped_lock _l(mLock);
 
     const auto infos = getLightInfoLocked(deviceId);
@@ -1116,7 +1158,7 @@
     return std::nullopt;
 }
 
-std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) {
+std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) const {
     std::scoped_lock _l(mLock);
 
     const auto infos = getLightInfoLocked(deviceId);
@@ -1133,7 +1175,7 @@
 }
 
 std::optional<std::unordered_map<LightColor, int32_t>> EventHub::getLightIntensities(
-        int32_t deviceId, int32_t lightId) {
+        int32_t deviceId, int32_t lightId) const {
     std::scoped_lock _l(mLock);
 
     const auto infos = getLightInfoLocked(deviceId);
@@ -1229,6 +1271,15 @@
     }
 }
 
+InputDeviceCountryCode EventHub::getCountryCode(int32_t deviceId) const {
+    std::scoped_lock _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr || !device->associatedDevice) {
+        return InputDeviceCountryCode::INVALID;
+    }
+    return device->associatedDevice->countryCode;
+}
+
 void EventHub::setExcludedDevices(const std::vector<std::string>& devices) {
     std::scoped_lock _l(mLock);
 
@@ -1358,6 +1409,39 @@
           identifier.descriptor.c_str());
 }
 
+std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDeviceLocked(
+        const std::filesystem::path& devicePath) const {
+    const std::optional<std::filesystem::path> sysfsRootPathOpt =
+            getSysfsRootPath(devicePath.c_str());
+    if (!sysfsRootPathOpt) {
+        return nullptr;
+    }
+
+    const auto& path = *sysfsRootPathOpt;
+
+    std::shared_ptr<const AssociatedDevice> associatedDevice = std::make_shared<AssociatedDevice>(
+            AssociatedDevice{.sysfsRootPath = path,
+                             .countryCode = readCountryCodeLocked(path),
+                             .batteryInfos = readBatteryConfiguration(path),
+                             .lightInfos = readLightsConfiguration(path)});
+
+    bool associatedDeviceChanged = false;
+    for (const auto& [id, dev] : mDevices) {
+        if (dev->associatedDevice && dev->associatedDevice->sysfsRootPath == path) {
+            if (*associatedDevice != *dev->associatedDevice) {
+                associatedDeviceChanged = true;
+                dev->associatedDevice = associatedDevice;
+            }
+            associatedDevice = dev->associatedDevice;
+        }
+    }
+    ALOGI_IF(associatedDeviceChanged,
+             "The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s",
+             path.c_str(), associatedDevice->dump().c_str());
+
+    return associatedDevice;
+}
+
 void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
     std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
@@ -1415,7 +1499,7 @@
     }
 }
 
-std::vector<int32_t> EventHub::getVibratorIds(int32_t deviceId) {
+std::vector<int32_t> EventHub::getVibratorIds(int32_t deviceId) const {
     std::scoped_lock _l(mLock);
     std::vector<int32_t> vibrators;
     Device* device = getDeviceLocked(deviceId);
@@ -1486,25 +1570,35 @@
 }
 
 std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t batteryId) const {
-    std::scoped_lock _l(mLock);
+    std::filesystem::path batteryPath;
+    {
+        // Do not read the sysfs node to get the battery state while holding
+        // the EventHub lock. For some peripheral devices, reading battery state
+        // can be broken and take 5+ seconds. Holding the lock in this case would
+        // block all other event processing during this time. For now, we assume this
+        // call never happens on the InputReader thread and read the sysfs node outside
+        // the lock to prevent event processing from being blocked by this call.
+        std::scoped_lock _l(mLock);
 
-    const auto infos = getBatteryInfoLocked(deviceId);
-    auto it = infos.find(batteryId);
-    if (it == infos.end()) {
-        return std::nullopt;
-    }
+        const auto& infos = getBatteryInfoLocked(deviceId);
+        auto it = infos.find(batteryId);
+        if (it == infos.end()) {
+            return std::nullopt;
+        }
+        batteryPath = it->second.path;
+    } // release lock
+
     std::string buffer;
 
     // Some devices report battery capacity as an integer through the "capacity" file
-    if (base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::CAPACITY),
+    if (base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::CAPACITY),
                                &buffer)) {
         return std::stoi(base::Trim(buffer));
     }
 
     // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
     // These values are taken from kernel source code include/linux/power_supply.h
-    if (base::ReadFileToString(it->second.path /
-                                       BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL),
+    if (base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL),
                                &buffer)) {
         // Remove any white space such as trailing new line
         const auto levelIt = BATTERY_LEVEL.find(base::Trim(buffer));
@@ -1517,15 +1611,27 @@
 }
 
 std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batteryId) const {
-    std::scoped_lock _l(mLock);
-    const auto infos = getBatteryInfoLocked(deviceId);
-    auto it = infos.find(batteryId);
-    if (it == infos.end()) {
-        return std::nullopt;
-    }
+    std::filesystem::path batteryPath;
+    {
+        // Do not read the sysfs node to get the battery state while holding
+        // the EventHub lock. For some peripheral devices, reading battery state
+        // can be broken and take 5+ seconds. Holding the lock in this case would
+        // block all other event processing during this time. For now, we assume this
+        // call never happens on the InputReader thread and read the sysfs node outside
+        // the lock to prevent event processing from being blocked by this call.
+        std::scoped_lock _l(mLock);
+
+        const auto& infos = getBatteryInfoLocked(deviceId);
+        auto it = infos.find(batteryId);
+        if (it == infos.end()) {
+            return std::nullopt;
+        }
+        batteryPath = it->second.path;
+    } // release lock
+
     std::string buffer;
 
-    if (!base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::STATUS),
+    if (!base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::STATUS),
                                 &buffer)) {
         ALOGE("Failed to read sysfs battery info: %s", strerror(errno));
         return std::nullopt;
@@ -1540,15 +1646,12 @@
     return std::nullopt;
 }
 
-size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
-    ALOG_ASSERT(bufferSize >= 1);
-
+std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) {
     std::scoped_lock _l(mLock);
 
-    struct input_event readBuffer[bufferSize];
+    std::array<input_event, EVENT_BUFFER_SIZE> readBuffer;
 
-    RawEvent* event = buffer;
-    size_t capacity = bufferSize;
+    std::vector<RawEvent> events;
     bool awoken = false;
     for (;;) {
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -1568,15 +1671,17 @@
         for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
             std::unique_ptr<Device> device = std::move(*it);
             ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
-            event->when = now;
-            event->deviceId = (device->id == mBuiltInKeyboardId)
+            const int32_t deviceId = (device->id == mBuiltInKeyboardId)
                     ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
                     : device->id;
-            event->type = DEVICE_REMOVED;
-            event += 1;
+            events.push_back({
+                    .when = now,
+                    .deviceId = deviceId,
+                    .type = DEVICE_REMOVED,
+            });
             it = mClosingDevices.erase(it);
             mNeedToSendFinishedDeviceScan = true;
-            if (--capacity == 0) {
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
@@ -1591,10 +1696,12 @@
             std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
             mOpeningDevices.pop_back();
             ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
-            event->when = now;
-            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
-            event->type = DEVICE_ADDED;
-            event += 1;
+            const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+            events.push_back({
+                    .when = now,
+                    .deviceId = deviceId,
+                    .type = DEVICE_ADDED,
+            });
 
             // Try to find a matching video device by comparing device names
             for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
@@ -1612,17 +1719,18 @@
                 ALOGW("Device id %d exists, replaced.", device->id);
             }
             mNeedToSendFinishedDeviceScan = true;
-            if (--capacity == 0) {
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
 
         if (mNeedToSendFinishedDeviceScan) {
             mNeedToSendFinishedDeviceScan = false;
-            event->when = now;
-            event->type = FINISHED_DEVICE_SCAN;
-            event += 1;
-            if (--capacity == 0) {
+            events.push_back({
+                    .when = now,
+                    .type = FINISHED_DEVICE_SCAN,
+            });
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
@@ -1686,12 +1794,13 @@
             // This must be an input event
             if (eventItem.events & EPOLLIN) {
                 int32_t readSize =
-                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
+                        read(device->fd, readBuffer.data(),
+                             sizeof(decltype(readBuffer)::value_type) * readBuffer.size());
                 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                     // Device was removed before INotify noticed.
                     ALOGW("could not get event, removed? (fd: %d size: %" PRId32
-                          " bufferSize: %zu capacity: %zu errno: %d)\n",
-                          device->fd, readSize, bufferSize, capacity, errno);
+                          " capacity: %zu errno: %d)\n",
+                          device->fd, readSize, readBuffer.size(), errno);
                     deviceChanged = true;
                     closeDeviceLocked(*device);
                 } else if (readSize < 0) {
@@ -1701,21 +1810,21 @@
                 } else if ((readSize % sizeof(struct input_event)) != 0) {
                     ALOGE("could not get event (wrong size: %d)", readSize);
                 } else {
-                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+                    const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
 
-                    size_t count = size_t(readSize) / sizeof(struct input_event);
+                    const size_t count = size_t(readSize) / sizeof(struct input_event);
                     for (size_t i = 0; i < count; i++) {
                         struct input_event& iev = readBuffer[i];
-                        event->when = processEventTimestamp(iev);
-                        event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
-                        event->deviceId = deviceId;
-                        event->type = iev.type;
-                        event->code = iev.code;
-                        event->value = iev.value;
-                        event += 1;
-                        capacity -= 1;
+                        events.push_back({
+                                .when = processEventTimestamp(iev),
+                                .readTime = systemTime(SYSTEM_TIME_MONOTONIC),
+                                .deviceId = deviceId,
+                                .type = iev.type,
+                                .code = iev.code,
+                                .value = iev.value,
+                        });
                     }
-                    if (capacity == 0) {
+                    if (events.size() >= EVENT_BUFFER_SIZE) {
                         // The result buffer is full.  Reset the pending event index
                         // so we will try to read the device again on the next iteration.
                         mPendingEventIndex -= 1;
@@ -1751,7 +1860,7 @@
         }
 
         // Return now if we have collected any events or if we were explicitly awoken.
-        if (event != buffer || awoken) {
+        if (!events.empty() || awoken) {
             break;
         }
 
@@ -1797,7 +1906,7 @@
     }
 
     // All done, return the number of events we read.
-    return event - buffer;
+    return events;
 }
 
 std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) {
@@ -2024,7 +2133,9 @@
 
     // Allocate device.  (The device object takes ownership of the fd at this point.)
     int32_t deviceId = mNextDeviceId++;
-    std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier);
+    std::unique_ptr<Device> device =
+            std::make_unique<Device>(fd, deviceId, devicePath, identifier,
+                                     obtainAssociatedDeviceLocked(devicePath));
 
     ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
     ALOGV("  bus:        %04x\n"
@@ -2042,27 +2153,6 @@
     // Load the configuration file for the device.
     device->loadConfigurationLocked();
 
-    bool hasBattery = false;
-    bool hasLights = false;
-    // Check the sysfs root path
-    std::optional<std::filesystem::path> sysfsRootPath = getSysfsRootPath(devicePath.c_str());
-    if (sysfsRootPath.has_value()) {
-        std::shared_ptr<AssociatedDevice> associatedDevice;
-        for (const auto& [id, dev] : mDevices) {
-            if (device->identifier.descriptor == dev->identifier.descriptor &&
-                !dev->associatedDevice) {
-                associatedDevice = dev->associatedDevice;
-            }
-        }
-        if (!associatedDevice) {
-            associatedDevice = std::make_shared<AssociatedDevice>(sysfsRootPath.value());
-        }
-        hasBattery = associatedDevice->configureBatteryLocked();
-        hasLights = associatedDevice->configureLightsLocked();
-
-        device->associatedDevice = associatedDevice;
-    }
-
     // Figure out the kinds of events the device reports.
     device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
     device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask);
@@ -2212,12 +2302,12 @@
     }
 
     // Classify InputDeviceClass::BATTERY.
-    if (hasBattery) {
+    if (device->associatedDevice && !device->associatedDevice->batteryInfos.empty()) {
         device->classes |= InputDeviceClass::BATTERY;
     }
 
     // Classify InputDeviceClass::LIGHT.
-    if (hasLights) {
+    if (device->associatedDevice && !device->associatedDevice->lightInfos.empty()) {
         device->classes |= InputDeviceClass::LIGHT;
     }
 
@@ -2285,7 +2375,7 @@
     return true;
 }
 
-bool EventHub::isDeviceEnabled(int32_t deviceId) {
+bool EventHub::isDeviceEnabled(int32_t deviceId) const {
     std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device == nullptr) {
@@ -2340,7 +2430,7 @@
 
     std::unique_ptr<Device> device =
             std::make_unique<Device>(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>",
-                                     identifier);
+                                     identifier, nullptr /*associatedDevice*/);
     device->classes = InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY |
             InputDeviceClass::DPAD | InputDeviceClass::VIRTUAL;
     device->loadKeyMapLocked();
@@ -2509,7 +2599,7 @@
     mNeedToReopenDevices = true;
 }
 
-void EventHub::dump(std::string& dump) {
+void EventHub::dump(std::string& dump) const {
     dump += "Event Hub State:\n";
 
     { // acquire lock
@@ -2542,14 +2632,18 @@
                                  device->keyMap.keyLayoutFile.c_str());
             dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
                                  device->keyMap.keyCharacterMapFile.c_str());
+            dump += StringPrintf(INDENT3 "CountryCode: %d\n",
+                                 device->associatedDevice ? device->associatedDevice->countryCode
+                                                          : InputDeviceCountryCode::INVALID);
             dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
                                  device->configurationFile.c_str());
-            dump += INDENT3 "VideoDevice: ";
-            if (device->videoDevice) {
-                dump += device->videoDevice->dump() + "\n";
-            } else {
-                dump += "<none>\n";
-            }
+            dump += StringPrintf(INDENT3 "VideoDevice: %s\n",
+                                 device->videoDevice ? device->videoDevice->dump().c_str()
+                                                     : "<none>");
+            dump += StringPrintf(INDENT3 "SysfsDevicePath: %s\n",
+                                 device->associatedDevice
+                                         ? device->associatedDevice->sysfsRootPath.c_str()
+                                         : "<none>");
         }
 
         dump += INDENT "Unattached video devices:\n";
@@ -2562,9 +2656,14 @@
     } // release lock
 }
 
-void EventHub::monitor() {
+void EventHub::monitor() const {
     // Acquire and release the lock to ensure that the event hub has not deadlocked.
     std::unique_lock<std::mutex> lock(mLock);
 }
 
-}; // namespace android
+std::string EventHub::AssociatedDevice::dump() const {
+    return StringPrintf("path=%s, numBatteries=%zu, numLight=%zu", sysfsRootPath.c_str(),
+                        batteryInfos.size(), lightInfos.size());
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index b67777f..5291776 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -35,6 +35,8 @@
 #include "SwitchInputMapper.h"
 #include "VibratorInputMapper.h"
 
+using android::hardware::input::InputDeviceCountryCode;
+
 namespace android {
 
 InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
@@ -63,7 +65,8 @@
     return enabled;
 }
 
-void InputDevice::setEnabled(bool enabled, nsecs_t when) {
+std::list<NotifyArgs> InputDevice::setEnabled(bool enabled, nsecs_t when) {
+    std::list<NotifyArgs> out;
     if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
         ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
               "but the corresponding viewport is not found",
@@ -72,7 +75,7 @@
     }
 
     if (isEnabled() == enabled) {
-        return;
+        return out;
     }
 
     // When resetting some devices, the driver needs to be queried to ensure that a proper reset is
@@ -80,13 +83,14 @@
     // but before disabling the device. See MultiTouchMotionAccumulator::reset for more information.
     if (enabled) {
         for_each_subdevice([](auto& context) { context.enableDevice(); });
-        reset(when);
+        out += reset(when);
     } else {
-        reset(when);
+        out += reset(when);
         for_each_subdevice([](auto& context) { context.disableDevice(); });
     }
     // Must change generation to flag this device as changed
     bumpGeneration();
+    return out;
 }
 
 void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
@@ -110,7 +114,8 @@
         dump += "<none>\n";
     }
     dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
-    dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+    dump += StringPrintf(INDENT2 "Sources: %s\n",
+                         inputEventSourceToString(deviceInfo.getSources()).c_str());
     dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
     dump += StringPrintf(INDENT2 "ControllerNum: %d\n", deviceInfo.getControllerNumber());
 
@@ -128,10 +133,10 @@
                 snprintf(name, sizeof(name), "%d", range.axis);
             }
             dump += StringPrintf(INDENT3
-                                 "%s: source=0x%08x, "
+                                 "%s: source=%s, "
                                  "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
-                                 name, range.source, range.min, range.max, range.flat, range.fuzz,
-                                 range.resolution);
+                                 name, inputEventSourceToString(range.source).c_str(), range.min,
+                                 range.max, range.flat, range.fuzz, range.resolution);
         }
     }
 
@@ -231,14 +236,20 @@
 }
 
 void InputDevice::removeEventHubDevice(int32_t eventHubId) {
+    if (mController != nullptr && mController->getEventHubId() == eventHubId) {
+        // Delete mController, since the corresponding eventhub device is going away
+        mController = nullptr;
+    }
     mDevices.erase(eventHubId);
 }
 
-void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
-                            uint32_t changes) {
+std::list<NotifyArgs> InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                             uint32_t changes) {
+    std::list<NotifyArgs> out;
     mSources = 0;
     mClasses = ftl::Flags<InputDeviceClass>(0);
     mControllerNumber = 0;
+    mCountryCode = InputDeviceCountryCode::INVALID;
 
     for_each_subdevice([this](InputDeviceContext& context) {
         mClasses |= context.getDeviceClasses();
@@ -250,6 +261,16 @@
             }
             mControllerNumber = controllerNumber;
         }
+
+        InputDeviceCountryCode countryCode = context.getCountryCode();
+        if (countryCode != InputDeviceCountryCode::INVALID) {
+            if (mCountryCode != InputDeviceCountryCode::INVALID && mCountryCode != countryCode) {
+                ALOGW("InputDevice::configure(): %s device contains multiple unique country "
+                      "codes",
+                      getName().c_str());
+            }
+            mCountryCode = countryCode;
+        }
     });
 
     mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
@@ -295,7 +316,7 @@
         if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
             auto it = config->disabledDevices.find(mId);
             bool enabled = it == config->disabledDevices.end();
-            setEnabled(enabled, when);
+            out += setEnabled(enabled, when);
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
@@ -348,37 +369,42 @@
                 // For first-time configuration, only allow device to be disabled after mappers have
                 // finished configuring. This is because we need to read some of the properties from
                 // the device's open fd.
-                setEnabled(enabled, when);
+                out += setEnabled(enabled, when);
             }
         }
 
-        for_each_mapper([this, when, config, changes](InputMapper& mapper) {
-            mapper.configure(when, config, changes);
+        for_each_mapper([this, when, &config, changes, &out](InputMapper& mapper) {
+            out += mapper.configure(when, config, changes);
             mSources |= mapper.getSources();
         });
 
         // If a device is just plugged but it might be disabled, we need to update some info like
         // axis range of touch from each InputMapper first, then disable it.
         if (!changes) {
-            setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
+            out += setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(),
+                              when);
         }
     }
+    return out;
 }
 
-void InputDevice::reset(nsecs_t when) {
-    for_each_mapper([when](InputMapper& mapper) { mapper.reset(when); });
+std::list<NotifyArgs> InputDevice::reset(nsecs_t when) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.reset(when); });
 
     mContext->updateGlobalMetaState();
 
-    notifyReset(when);
+    out.push_back(notifyReset(when));
+    return out;
 }
 
-void InputDevice::process(const RawEvent* rawEvents, size_t count) {
+std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) {
     // Process all of the events in order for each mapper.
     // We cannot simply ask each mapper to process them in bulk because mappers may
     // have side-effects that must be interleaved.  For example, joystick movement events and
     // gamepad button presses are handled by different mappers but they should be dispatched
     // in the order received.
+    std::list<NotifyArgs> out;
     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,
@@ -400,28 +426,33 @@
         } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
             ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
             mDropUntilNextSync = true;
-            reset(rawEvent->when);
+            out += reset(rawEvent->when);
         } else {
-            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
-                mapper.process(rawEvent);
+            for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
+                out += mapper.process(rawEvent);
             });
         }
         --count;
     }
+    return out;
 }
 
-void InputDevice::timeoutExpired(nsecs_t when) {
-    for_each_mapper([when](InputMapper& mapper) { mapper.timeoutExpired(when); });
+std::list<NotifyArgs> InputDevice::timeoutExpired(nsecs_t when) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.timeoutExpired(when); });
+    return out;
 }
 
-void InputDevice::updateExternalStylusState(const StylusState& state) {
-    for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); });
+std::list<NotifyArgs> InputDevice::updateExternalStylusState(const StylusState& state) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.updateExternalStylusState(state); });
+    return out;
 }
 
 InputDeviceInfo InputDevice::getDeviceInfo() {
     InputDeviceInfo outDeviceInfo;
     outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
-                             mHasMic);
+                             mHasMic, mCountryCode);
     for_each_mapper(
             [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(&outDeviceInfo); });
 
@@ -493,14 +524,17 @@
     return *result;
 }
 
-void InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {
-    for_each_mapper([sequence, repeat, token](InputMapper& mapper) {
-        mapper.vibrate(sequence, repeat, token);
-    });
+std::list<NotifyArgs> InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat,
+                                           int32_t token) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.vibrate(sequence, repeat, token); });
+    return out;
 }
 
-void InputDevice::cancelVibrate(int32_t token) {
-    for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); });
+std::list<NotifyArgs> InputDevice::cancelVibrate(int32_t token) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.cancelVibrate(token); });
+    return out;
 }
 
 bool InputDevice::isVibrating() {
@@ -543,16 +577,10 @@
     for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); });
 }
 
-void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
-    for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); });
-}
-
-std::optional<int32_t> InputDevice::getBatteryCapacity() {
-    return mController ? mController->getBatteryCapacity(DEFAULT_BATTERY_ID) : std::nullopt;
-}
-
-std::optional<int32_t> InputDevice::getBatteryStatus() {
-    return mController ? mController->getBatteryStatus(DEFAULT_BATTERY_ID) : std::nullopt;
+std::list<NotifyArgs> InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.cancelTouch(when, readTime); });
+    return out;
 }
 
 bool InputDevice::setLightColor(int32_t lightId, int32_t color) {
@@ -591,9 +619,8 @@
     mGeneration = mContext->bumpGeneration();
 }
 
-void InputDevice::notifyReset(nsecs_t when) {
-    NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
-    mContext->getListener().notifyDeviceReset(&args);
+NotifyDeviceResetArgs InputDevice::notifyReset(nsecs_t when) {
+    return NotifyDeviceResetArgs(mContext->getNextId(), when, mId);
 }
 
 std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
@@ -622,6 +649,10 @@
     for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); });
 }
 
+std::optional<int32_t> InputDevice::getBatteryEventHubId() const {
+    return mController ? std::make_optional(mController->getEventHubId()) : std::nullopt;
+}
+
 InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
       : mDevice(device),
         mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 11a2dbe..428e999 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -120,14 +120,14 @@
         }
     } // release lock
 
-    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
+    std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);
 
     { // acquire lock
         std::scoped_lock _l(mLock);
         mReaderIsAliveCondition.notify_all();
 
-        if (count) {
-            processEventsLocked(mEventBuffer, count);
+        if (!events.empty()) {
+            notifyAll(processEventsLocked(events.data(), events.size()));
         }
 
         if (mNextTimeout != LLONG_MAX) {
@@ -137,7 +137,7 @@
                     ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
                 }
                 mNextTimeout = LLONG_MAX;
-                timeoutExpiredLocked(now);
+                notifyAll(timeoutExpiredLocked(now));
             }
         }
 
@@ -162,7 +162,8 @@
     mQueuedListener.flush();
 }
 
-void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
+std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
+    std::list<NotifyArgs> out;
     for (const RawEvent* rawEvent = rawEvents; count;) {
         int32_t type = rawEvent->type;
         size_t batchSize = 1;
@@ -178,7 +179,7 @@
             if (DEBUG_RAW_EVENTS) {
                 ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
             }
-            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
+            out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
         } else {
             switch (rawEvent->type) {
                 case EventHubInterface::DEVICE_ADDED:
@@ -198,6 +199,7 @@
         count -= batchSize;
         rawEvent += batchSize;
     }
+    return out;
 }
 
 void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
@@ -208,17 +210,18 @@
 
     InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
     std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
-    device->configure(when, &mConfig, 0);
-    device->reset(when);
+
+    notifyAll(device->configure(when, &mConfig, 0));
+    notifyAll(device->reset(when));
 
     if (device->isIgnored()) {
         ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
               "(ignored non-input device)",
               device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str());
     } else {
-        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x",
+        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=%s",
               device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),
-              device->getSources());
+              inputEventSourceToString(device->getSources()).c_str());
     }
 
     mDevices.emplace(eventHubId, device);
@@ -270,9 +273,10 @@
               device->getId(), eventHubId, device->getName().c_str(),
               device->getDescriptor().c_str());
     } else {
-        ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=0x%08x",
+        ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=%s",
               device->getId(), eventHubId, device->getName().c_str(),
-              device->getDescriptor().c_str(), device->getSources());
+              device->getDescriptor().c_str(),
+              inputEventSourceToString(device->getSources()).c_str());
     }
 
     device->removeEventHubDevice(eventHubId);
@@ -281,10 +285,12 @@
         notifyExternalStylusPresenceChangedLocked();
     }
 
+    std::list<NotifyArgs> resetEvents;
     if (device->hasEventHubDevices()) {
-        device->configure(when, &mConfig, 0);
+        resetEvents += device->configure(when, &mConfig, 0);
     }
-    device->reset(when);
+    resetEvents += device->reset(when);
+    notifyAll(std::move(resetEvents));
 }
 
 std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
@@ -307,21 +313,22 @@
     return device;
 }
 
-void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
-                                               size_t count) {
+std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId,
+                                                                const RawEvent* rawEvents,
+                                                                size_t count) {
     auto deviceIt = mDevices.find(eventHubId);
     if (deviceIt == mDevices.end()) {
         ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
-        return;
+        return {};
     }
 
     std::shared_ptr<InputDevice>& device = deviceIt->second;
     if (device->isIgnored()) {
         // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
-        return;
+        return {};
     }
 
-    device->process(rawEvents, count);
+    return device->process(rawEvents, count);
 }
 
 InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) const {
@@ -335,13 +342,15 @@
     return nullptr;
 }
 
-void InputReader::timeoutExpiredLocked(nsecs_t when) {
+std::list<NotifyArgs> InputReader::timeoutExpiredLocked(nsecs_t when) {
+    std::list<NotifyArgs> out;
     for (auto& devicePair : mDevices) {
         std::shared_ptr<InputDevice>& device = devicePair.second;
         if (!device->isIgnored()) {
-            device->timeoutExpired(when);
+            out += device->timeoutExpired(when);
         }
     }
+    return out;
 }
 
 int32_t InputReader::nextInputDeviceIdLocked() {
@@ -376,7 +385,7 @@
     } else {
         for (auto& devicePair : mDevices) {
             std::shared_ptr<InputDevice>& device = devicePair.second;
-            device->configure(now, &mConfig, changes);
+            notifyAll(device->configure(now, &mConfig, changes));
         }
     }
 
@@ -393,6 +402,12 @@
     }
 }
 
+void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) {
+    for (const NotifyArgs& args : argsList) {
+        mQueuedListener.notify(args);
+    }
+}
+
 void InputReader::updateGlobalMetaStateLocked() {
     mGlobalMetaState = 0;
 
@@ -431,11 +446,13 @@
     }
 }
 
-void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
+std::list<NotifyArgs> InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
+    std::list<NotifyArgs> out;
     for (auto& devicePair : mDevices) {
         std::shared_ptr<InputDevice>& device = devicePair.second;
-        device->updateExternalStylusState(state);
+        out += device->updateExternalStylusState(state);
     }
+    return out;
 }
 
 void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
@@ -641,7 +658,7 @@
 
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (device) {
-        device->vibrate(sequence, repeat, token);
+        notifyAll(device->vibrate(sequence, repeat, token));
     }
 }
 
@@ -650,7 +667,7 @@
 
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (device) {
-        device->cancelVibrate(token);
+        notifyAll(device->cancelVibrate(token));
     }
 }
 
@@ -705,23 +722,71 @@
 }
 
 std::optional<int32_t> InputReader::getBatteryCapacity(int32_t deviceId) {
-    std::scoped_lock _l(mLock);
+    std::optional<int32_t> eventHubId;
+    {
+        // Do not query the battery state while holding the lock. For some peripheral devices,
+        // reading battery state can be broken and take 5+ seconds. Holding the lock in this case
+        // would block all other event processing during this time. For now, we assume this
+        // call never happens on the InputReader thread and get the battery state outside the
+        // lock to prevent event processing from being blocked by this call.
+        std::scoped_lock _l(mLock);
+        InputDevice* device = findInputDeviceLocked(deviceId);
+        if (!device) return {};
+        eventHubId = device->getBatteryEventHubId();
+    } // release lock
 
-    InputDevice* device = findInputDeviceLocked(deviceId);
-    if (device) {
-        return device->getBatteryCapacity();
+    if (!eventHubId) return {};
+    const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId);
+    if (batteryIds.empty()) {
+        ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId);
+        return {};
     }
-    return std::nullopt;
+    return mEventHub->getBatteryCapacity(*eventHubId, batteryIds.front());
 }
 
 std::optional<int32_t> InputReader::getBatteryStatus(int32_t deviceId) {
+    std::optional<int32_t> eventHubId;
+    {
+        // Do not query the battery state while holding the lock. For some peripheral devices,
+        // reading battery state can be broken and take 5+ seconds. Holding the lock in this case
+        // would block all other event processing during this time. For now, we assume this
+        // call never happens on the InputReader thread and get the battery state outside the
+        // lock to prevent event processing from being blocked by this call.
+        std::scoped_lock _l(mLock);
+        InputDevice* device = findInputDeviceLocked(deviceId);
+        if (!device) return {};
+        eventHubId = device->getBatteryEventHubId();
+    } // release lock
+
+    if (!eventHubId) return {};
+    const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId);
+    if (batteryIds.empty()) {
+        ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId);
+        return {};
+    }
+    return mEventHub->getBatteryStatus(*eventHubId, batteryIds.front());
+}
+
+std::optional<std::string> InputReader::getBatteryDevicePath(int32_t deviceId) {
     std::scoped_lock _l(mLock);
 
     InputDevice* device = findInputDeviceLocked(deviceId);
-    if (device) {
-        return device->getBatteryStatus();
+    if (!device) return {};
+
+    std::optional<int32_t> eventHubId = device->getBatteryEventHubId();
+    if (!eventHubId) return {};
+    const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId);
+    if (batteryIds.empty()) {
+        ALOGW("%s: There are no battery ids for EventHub device %d", __func__, *eventHubId);
+        return {};
     }
-    return std::nullopt;
+    const auto batteryInfo = mEventHub->getRawBatteryInfo(*eventHubId, batteryIds.front());
+    if (!batteryInfo) {
+        ALOGW("%s: Failed to get RawBatteryInfo for battery %d of EventHub device %d", __func__,
+              batteryIds.front(), *eventHubId);
+        return {};
+    }
+    return batteryInfo->path;
 }
 
 std::vector<InputDeviceLightInfo> InputReader::getLights(int32_t deviceId) {
@@ -966,18 +1031,15 @@
     mReader->getExternalStylusDevicesLocked(outDevices);
 }
 
-void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
-    mReader->dispatchExternalStylusStateLocked(state);
+std::list<NotifyArgs> InputReader::ContextImpl::dispatchExternalStylusState(
+        const StylusState& state) {
+    return mReader->dispatchExternalStylusStateLocked(state);
 }
 
 InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
     return mReader->mPolicy.get();
 }
 
-InputListenerInterface& InputReader::ContextImpl::getListener() {
-    return mReader->mQueuedListener;
-}
-
 EventHubInterface* InputReader::ContextImpl::getEventHub() {
     return mReader->mEventHub.get();
 }
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index 1bbf386..e107d88 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_MACROS_H
-#define _UI_INPUTREADER_MACROS_H
+#pragma once
 
 #define LOG_TAG "InputReader"
 
@@ -115,5 +114,3 @@
 }
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_MACROS_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 7673174..cedbacb 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -16,6 +16,7 @@
 
 #include <locale>
 #include <regex>
+#include <set>
 
 #include <ftl/enum.h>
 
@@ -70,7 +71,7 @@
 
     // If the light node doesn't have max brightness, use the default max brightness.
     int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness;
     // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
     if (rawMaxBrightness != MAX_BRIGHTNESS) {
         brightness = brightness * ratio;
@@ -89,7 +90,7 @@
     }
     // If the light node doesn't have max brightness, use the default max brightness.
     int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness;
     // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
     if (rawMaxBrightness != MAX_BRIGHTNESS) {
         brightness = ceil(brightness / ratio);
@@ -271,7 +272,8 @@
 
     for (const auto& [lightId, light] : mLights) {
         // Input device light doesn't support ordinal, always pass 1.
-        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
+        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, light->capabilityFlags,
+                                       1 /* ordinal */);
         deviceInfo->addLightInfo(lightInfo);
     }
 }
@@ -284,6 +286,8 @@
             dump += StringPrintf(INDENT4 "Id: %d", lightId);
             dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
             dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str());
+            dump += StringPrintf(INDENT4 "Capability flags: %s",
+                                 light->capabilityFlags.string().c_str());
             light->dump(dump);
         }
     }
@@ -363,6 +367,8 @@
     std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
     // Map from player Id to raw light Id
     std::unordered_map<int32_t, int32_t> playerIdLightIds;
+    // Set of Keyboard backlights
+    std::set<int32_t> keyboardBacklightIds;
 
     // Check raw lights
     const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
@@ -391,6 +397,10 @@
                 }
             }
         }
+        // Check if this is a Keyboard backlight
+        if (rawInfo->flags.test(InputLightClass::KEYBOARD_BACKLIGHT)) {
+            keyboardBacklightIds.insert(rawId);
+        }
         // Check if this is an LED of RGB light
         if (rawInfo->flags.test(InputLightClass::RED)) {
             hasRedLed = true;
@@ -431,8 +441,21 @@
             ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
                   rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
         }
+        bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) !=
+                        keyboardBacklightIds.end() &&
+                keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) !=
+                        keyboardBacklightIds.end() &&
+                keyboardBacklightIds.find(rawRgbIds.at(LightColor::BLUE)) !=
+                        keyboardBacklightIds.end() &&
+                (!rawGlobalId.has_value() ||
+                 keyboardBacklightIds.find(rawGlobalId.value()) != keyboardBacklightIds.end());
+
         std::unique_ptr<Light> light =
-                std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, rawRgbIds, rawGlobalId);
+                std::make_unique<RgbLight>(getDeviceContext(), ++mNextId,
+                                           isKeyboardBacklight
+                                                   ? InputDeviceLightType::KEYBOARD_BACKLIGHT
+                                                   : InputDeviceLightType::INPUT,
+                                           rawRgbIds, rawGlobalId);
         mLights.insert_or_assign(light->id, std::move(light));
         // Remove from raw light info as they've been composed a RBG light.
         rawInfos.erase(rawRgbIds.at(LightColor::RED));
@@ -445,6 +468,10 @@
 
     // Check the rest of raw light infos
     for (const auto& [rawId, rawInfo] : rawInfos) {
+        InputDeviceLightType type = keyboardBacklightIds.find(rawId) != keyboardBacklightIds.end()
+                ? InputDeviceLightType::KEYBOARD_BACKLIGHT
+                : InputDeviceLightType::INPUT;
+
         // If the node is multi-color led, construct a MULTI_COLOR light
         if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
             rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
@@ -453,7 +480,7 @@
             }
             std::unique_ptr<Light> light =
                     std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
-                                                      rawInfo.id);
+                                                      type, rawInfo.id);
             mLights.insert_or_assign(light->id, std::move(light));
             continue;
         }
@@ -462,7 +489,7 @@
             ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
         }
         std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name,
-                                                                   ++mNextId, rawInfo.id);
+                                                                   ++mNextId, type, rawInfo.id);
 
         mLights.insert_or_assign(light->id, std::move(light));
     }
@@ -521,4 +548,8 @@
     return light->getLightPlayerId();
 }
 
+int32_t PeripheralController::getEventHubId() const {
+    return getDeviceContext().getEventHubId();
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h
index b1bc8c7..8ac42c3 100644
--- a/services/inputflinger/reader/controller/PeripheralController.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_LIGHT_CONTROLLER_H
-#define _UI_INPUTREADER_LIGHT_CONTROLLER_H
+#pragma once
 
 #include "PeripheralControllerInterface.h"
 
@@ -31,6 +30,7 @@
     explicit PeripheralController(InputDeviceContext& deviceContext);
     ~PeripheralController() override;
 
+    int32_t getEventHubId() const override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
     bool setLightColor(int32_t lightId, int32_t color) override;
@@ -43,6 +43,7 @@
 private:
     inline int32_t getDeviceId() { return mDeviceContext.getId(); }
     inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+    inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; }
 
     InputDeviceContext& mDeviceContext;
     void configureLights();
@@ -66,6 +67,7 @@
         std::string name;
         int32_t id;
         InputDeviceLightType type;
+        ftl::Flags<InputDeviceLightCapability> capabilityFlags;
 
         virtual bool setLightColor(int32_t color) { return false; }
         virtual std::optional<int32_t> getLightColor() { return std::nullopt; }
@@ -80,8 +82,10 @@
 
     struct MonoLight : public Light {
         explicit MonoLight(InputDeviceContext& context, const std::string& name, int32_t id,
-                           int32_t rawId)
-              : Light(context, name, id, InputDeviceLightType::MONO), rawId(rawId) {}
+                           InputDeviceLightType type, int32_t rawId)
+              : Light(context, name, id, type), rawId(rawId) {
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+        }
         int32_t rawId;
 
         bool setLightColor(int32_t color) override;
@@ -90,15 +94,15 @@
     };
 
     struct RgbLight : public Light {
-        explicit RgbLight(InputDeviceContext& context, int32_t id,
+        explicit RgbLight(InputDeviceContext& context, int32_t id, InputDeviceLightType type,
                           const std::unordered_map<LightColor, int32_t>& rawRgbIds,
                           std::optional<int32_t> rawGlobalId)
-              : Light(context, "RGB", id, InputDeviceLightType::RGB),
-                rawRgbIds(rawRgbIds),
-                rawGlobalId(rawGlobalId) {
+              : Light(context, "RGB", id, type), rawRgbIds(rawRgbIds), rawGlobalId(rawGlobalId) {
             brightness = rawGlobalId.has_value()
                     ? getRawLightBrightness(rawGlobalId.value()).value_or(MAX_BRIGHTNESS)
                     : MAX_BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::RGB;
         }
         // Map from color to raw light id.
         std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
@@ -113,8 +117,11 @@
 
     struct MultiColorLight : public Light {
         explicit MultiColorLight(InputDeviceContext& context, const std::string& name, int32_t id,
-                                 int32_t rawId)
-              : Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {}
+                                 InputDeviceLightType type, int32_t rawId)
+              : Light(context, name, id, type), rawId(rawId) {
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::RGB;
+        }
         int32_t rawId;
 
         bool setLightColor(int32_t color) override;
@@ -130,7 +137,7 @@
         // Map from player Id to raw light Id
         std::unordered_map<int32_t, int32_t> rawLightIds;
 
-        bool setLightPlayerId(int32_t palyerId) override;
+        bool setLightPlayerId(int32_t playerId) override;
         std::optional<int32_t> getLightPlayerId() override;
         void dump(std::string& dump) override;
     };
@@ -148,5 +155,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_LIGHT_CONTROLLER_H
diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
index 7688a43..76ed1ca 100644
--- a/services/inputflinger/reader/controller/PeripheralControllerInterface.h
+++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_CONTROLLER_H
-#define _UI_INPUTREADER_INPUT_CONTROLLER_H
+#pragma once
 
 #include "EventHub.h"
 #include "InputDevice.h"
@@ -33,6 +32,8 @@
     PeripheralControllerInterface() {}
     virtual ~PeripheralControllerInterface() {}
 
+    virtual int32_t getEventHubId() const = 0;
+
     // Interface methods for Battery
     virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0;
     virtual std::optional<int32_t> getBatteryStatus(int32_t batteryId) = 0;
@@ -48,5 +49,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_INPUT_CONTROLLER_H
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 4733ecb..6933ec7 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _RUNTIME_EVENT_HUB_H
-#define _RUNTIME_EVENT_HUB_H
+#pragma once
 
 #include <bitset>
 #include <climits>
 #include <filesystem>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include <batteryservice/BatteryService.h>
@@ -42,6 +42,7 @@
 
 #include "TouchVideoDevice.h"
 #include "VibrationElement.h"
+#include "android/hardware/input/InputDeviceCountryCode.h"
 
 struct inotify_event;
 
@@ -173,6 +174,8 @@
     MULTI_INTENSITY = 0x00000040,
     /* The input light has max brightness node. */
     MAX_BRIGHTNESS = 0x00000080,
+    /* The input light has kbd_backlight name */
+    KEYBOARD_BACKLIGHT = 0x00000100,
 };
 
 enum class InputBatteryClass : uint32_t {
@@ -192,6 +195,9 @@
     ftl::Flags<InputLightClass> flags;
     std::array<int32_t, COLOR_NUM> rgbIndex;
     std::filesystem::path path;
+
+    bool operator==(const RawLightInfo&) const = default;
+    bool operator!=(const RawLightInfo&) const = default;
 };
 
 /* Describes a raw battery. */
@@ -200,6 +206,9 @@
     std::string name;
     ftl::Flags<InputBatteryClass> flags;
     std::filesystem::path path;
+
+    bool operator==(const RawBatteryInfo&) const = default;
+    bool operator!=(const RawBatteryInfo&) const = default;
 };
 
 /*
@@ -279,29 +288,30 @@
      *
      * Returns the number of events obtained, or 0 if the timeout expired.
      */
-    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
+    virtual std::vector<RawEvent> getEvents(int timeoutMillis) = 0;
     virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
-    virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
-                                                                              int32_t absCode) = 0;
+    virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+            int32_t deviceId, int32_t absCode) const = 0;
     // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node,
     // containing the raw info of the sysfs node structure.
-    virtual const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) = 0;
+    virtual std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const = 0;
     virtual std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
-                                                            int32_t BatteryId) = 0;
+                                                            int32_t BatteryId) const = 0;
 
     // Raw lights are sysfs led light nodes we found from the EventHub device sysfs node,
     // containing the raw info of the sysfs node structure.
-    virtual const std::vector<int32_t> getRawLightIds(int32_t deviceId) = 0;
-    virtual std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) = 0;
-    virtual std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) = 0;
+    virtual std::vector<int32_t> getRawLightIds(int32_t deviceId) const = 0;
+    virtual std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId,
+                                                        int32_t lightId) const = 0;
+    virtual std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const = 0;
     virtual void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) = 0;
     virtual std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
-            int32_t deviceId, int32_t lightId) = 0;
+            int32_t deviceId, int32_t lightId) const = 0;
     virtual void setLightIntensities(int32_t deviceId, int32_t lightId,
                                      std::unordered_map<LightColor, int32_t> intensities) = 0;
-    /*
-     * Query current input state.
-     */
+    /* Query Country code associated with the input device. */
+    virtual hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const = 0;
+    /* Query current input state. */
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
     virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
@@ -332,7 +342,7 @@
     /* Control the vibrator. */
     virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0;
     virtual void cancelVibrate(int32_t deviceId) = 0;
-    virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
+    virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) const = 0;
 
     /* Query battery level. */
     virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
@@ -348,13 +358,13 @@
     virtual void wake() = 0;
 
     /* Dump EventHub state to a string. */
-    virtual void dump(std::string& dump) = 0;
+    virtual void dump(std::string& dump) const = 0;
 
     /* Called by the heatbeat to ensures that the reader has not deadlocked. */
-    virtual void monitor() = 0;
+    virtual void monitor() const = 0;
 
     /* Return true if the device is enabled. */
-    virtual bool isDeviceEnabled(int32_t deviceId) = 0;
+    virtual bool isDeviceEnabled(int32_t deviceId) const = 0;
 
     /* Enable an input device */
     virtual status_t enableDevice(int32_t deviceId) = 0;
@@ -462,23 +472,27 @@
                      AxisInfo* outAxisInfo) const override final;
 
     base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
-            int32_t deviceId, int32_t absCode) override final;
+            int32_t deviceId, int32_t absCode) const override final;
 
-    const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override final;
+    std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override final;
     std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
-                                                    int32_t BatteryId) override final;
+                                                    int32_t BatteryId) const override final;
 
-    const std::vector<int32_t> getRawLightIds(int32_t deviceId) override final;
+    std::vector<int32_t> getRawLightIds(int32_t deviceId) const override final;
 
-    std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override final;
+    std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId,
+                                                int32_t lightId) const override final;
 
-    std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override final;
+    std::optional<int32_t> getLightBrightness(int32_t deviceId,
+                                              int32_t lightId) const override final;
     void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override final;
     std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
-            int32_t deviceId, int32_t lightId) override final;
+            int32_t deviceId, int32_t lightId) const override final;
     void setLightIntensities(int32_t deviceId, int32_t lightId,
                              std::unordered_map<LightColor, int32_t> intensities) override final;
 
+    hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const override final;
+
     void setExcludedDevices(const std::vector<std::string>& devices) override final;
 
     int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final;
@@ -492,7 +506,7 @@
     bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
                                uint8_t* outFlags) const override final;
 
-    size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override final;
+    std::vector<RawEvent> getEvents(int timeoutMillis) override final;
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final;
 
     bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final;
@@ -511,15 +525,15 @@
 
     void vibrate(int32_t deviceId, const VibrationElement& effect) override final;
     void cancelVibrate(int32_t deviceId) override final;
-    std::vector<int32_t> getVibratorIds(int32_t deviceId) override final;
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) const override final;
 
     void requestReopenDevices() override final;
 
     void wake() override final;
 
-    void dump(std::string& dump) override final;
+    void dump(std::string& dump) const override final;
 
-    void monitor() override final;
+    void monitor() const override final;
 
     std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
                                               int32_t batteryId) const override final;
@@ -527,7 +541,7 @@
     std::optional<int32_t> getBatteryStatus(int32_t deviceId,
                                             int32_t batteryId) const override final;
 
-    bool isDeviceEnabled(int32_t deviceId) override final;
+    bool isDeviceEnabled(int32_t deviceId) const override final;
 
     status_t enableDevice(int32_t deviceId) override final;
 
@@ -536,20 +550,17 @@
     ~EventHub() override;
 
 private:
+    // Holds information about the sysfs device associated with the Device.
     struct AssociatedDevice {
-        // The device descriptor from evdev device the misc device associated with.
-        std::string descriptor;
         // The sysfs root path of the misc device.
         std::filesystem::path sysfsRootPath;
+        hardware::input::InputDeviceCountryCode countryCode;
+        std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos;
+        std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
 
-        int32_t nextBatteryId;
-        int32_t nextLightId;
-        std::unordered_map<int32_t, RawBatteryInfo> batteryInfos;
-        std::unordered_map<int32_t, RawLightInfo> lightInfos;
-        explicit AssociatedDevice(std::filesystem::path sysfsRootPath)
-              : sysfsRootPath(sysfsRootPath), nextBatteryId(0), nextLightId(0) {}
-        bool configureBatteryLocked();
-        bool configureLightsLocked();
+        bool operator==(const AssociatedDevice&) const = default;
+        bool operator!=(const AssociatedDevice&) const = default;
+        std::string dump() const;
     };
 
     struct Device {
@@ -582,13 +593,13 @@
         int16_t ffEffectId; // initially -1
 
         // A shared_ptr of a device associated with the input device.
-        // The input devices with same descriptor has the same associated device.
-        std::shared_ptr<AssociatedDevice> associatedDevice;
+        // The input devices that have the same sysfs path have the same associated device.
+        std::shared_ptr<const AssociatedDevice> associatedDevice;
 
         int32_t controllerNumber;
 
-        Device(int fd, int32_t id, const std::string& path,
-               const InputDeviceIdentifier& identifier);
+        Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier,
+               std::shared_ptr<const AssociatedDevice> assocDev);
         ~Device();
 
         void close();
@@ -633,6 +644,8 @@
     void createVirtualKeyboardLocked() REQUIRES(mLock);
     void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock);
     void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock);
+    std::shared_ptr<const AssociatedDevice> obtainAssociatedDeviceLocked(
+            const std::filesystem::path& devicePath) const REQUIRES(mLock);
 
     void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
     void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
@@ -730,5 +743,3 @@
 };
 
 } // namespace android
-
-#endif // _RUNTIME_EVENT_HUB_H
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 51872ac..afb1bed 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_DEVICE_H
-#define _UI_INPUTREADER_INPUT_DEVICE_H
+#pragma once
 
 #include <ftl/flags.h>
 #include <input/DisplayViewport.h>
@@ -30,10 +29,9 @@
 #include "EventHub.h"
 #include "InputReaderBase.h"
 #include "InputReaderContext.h"
+#include "NotifyArgs.h"
 
 namespace android {
-// TODO b/180733860 support multiple battery in API and remove this.
-constexpr int32_t DEFAULT_BATTERY_ID = 1;
 
 class PeripheralController;
 class PeripheralControllerInterface;
@@ -72,16 +70,18 @@
     inline bool isIgnored() { return !getMapperCount(); }
 
     bool isEnabled();
-    void setEnabled(bool enabled, nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> setEnabled(bool enabled, nsecs_t when);
 
     void dump(std::string& dump, const std::string& eventHubDevStr);
     void addEventHubDevice(int32_t eventHubId, bool populateMappers = true);
     void removeEventHubDevice(int32_t eventHubId);
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    void reset(nsecs_t when);
-    void process(const RawEvent* rawEvents, size_t count);
-    void timeoutExpired(nsecs_t when);
-    void updateExternalStylusState(const StylusState& state);
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes);
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvents, size_t count);
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
 
     InputDeviceInfo getDeviceInfo();
     int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
@@ -90,18 +90,18 @@
     int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const;
     bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
                                uint8_t* outFlags);
-    void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
-    void cancelVibrate(int32_t token);
+    [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
+                                                int32_t token);
+    [[nodiscard]] std::list<NotifyArgs> cancelVibrate(int32_t token);
     bool isVibrating();
     std::vector<int32_t> getVibratorIds();
-    void cancelTouch(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime);
     bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
                       std::chrono::microseconds maxBatchReportLatency);
     void disableSensor(InputDeviceSensorType sensorType);
     void flushSensor(InputDeviceSensorType sensorType);
 
-    std::optional<int32_t> getBatteryCapacity();
-    std::optional<int32_t> getBatteryStatus();
+    std::optional<int32_t> getBatteryEventHubId() const;
 
     bool setLightColor(int32_t lightId, int32_t color);
     bool setLightPlayerId(int32_t lightId, int32_t playerId);
@@ -113,7 +113,7 @@
 
     void bumpGeneration();
 
-    void notifyReset(nsecs_t when);
+    [[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
 
     inline const PropertyMap& getConfiguration() { return mConfiguration; }
     inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
@@ -158,6 +158,7 @@
     int32_t mId;
     int32_t mGeneration;
     int32_t mControllerNumber;
+    hardware::input::InputDeviceCountryCode mCountryCode;
     InputDeviceIdentifier mIdentifier;
     std::string mAlias;
     ftl::Flags<InputDeviceClass> mClasses;
@@ -311,6 +312,9 @@
     }
 
     inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); }
+    inline hardware::input::InputDeviceCountryCode getCountryCode() const {
+        return mEventHub->getCountryCode(mId);
+    }
     inline int32_t getScanCodeState(int32_t scanCode) const {
         return mEventHub->getScanCodeState(mId, scanCode);
     }
@@ -395,7 +399,9 @@
     inline std::optional<DisplayViewport> getAssociatedViewport() const {
         return mDevice.getAssociatedViewport();
     }
-    inline void cancelTouch(nsecs_t when, nsecs_t readTime) { mDevice.cancelTouch(when, readTime); }
+    [[nodiscard]] inline std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) {
+        return mDevice.cancelTouch(when, readTime);
+    }
     inline void bumpGeneration() { mDevice.bumpGeneration(); }
     inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
 
@@ -408,5 +414,3 @@
 };
 
 } // namespace android
-
-#endif //_UI_INPUTREADER_INPUT_DEVICE_H
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index ae41e01..de268cf 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_READER_H
-#define _UI_INPUTREADER_INPUT_READER_H
+#pragma once
 
 #include <PointerControllerInterface.h>
 #include <android-base/thread_annotations.h>
@@ -100,6 +99,8 @@
 
     std::optional<int32_t> getBatteryStatus(int32_t deviceId) override;
 
+    std::optional<std::string> getBatteryDevicePath(int32_t deviceId) override;
+
     std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) override;
 
     std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) override;
@@ -141,10 +142,9 @@
         int32_t bumpGeneration() NO_THREAD_SAFETY_ANALYSIS override;
         void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices)
                 REQUIRES(mReader->mLock) override;
-        void dispatchExternalStylusState(const StylusState& outState)
+        [[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusState(const StylusState& outState)
                 REQUIRES(mReader->mLock) override;
         InputReaderPolicyInterface* getPolicy() 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;
@@ -169,10 +169,6 @@
 
     InputReaderConfiguration mConfig GUARDED_BY(mLock);
 
-    // The event queue.
-    static const int EVENT_BUFFER_SIZE = 256;
-    RawEvent mEventBuffer[EVENT_BUFFER_SIZE] GUARDED_BY(mLock);
-
     // An input device can represent a collection of EventHub devices. This map provides a way
     // to lookup the input device instance from the EventHub device id.
     std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices
@@ -184,13 +180,15 @@
             mDeviceToEventHubIdsMap GUARDED_BY(mLock);
 
     // low-level input event decoding and device management
-    void processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock);
+    [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
+            REQUIRES(mLock);
 
     void addDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock);
     void removeDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock);
-    void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count)
-            REQUIRES(mLock);
-    void timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
+    [[nodiscard]] std::list<NotifyArgs> processEventsForDeviceLocked(int32_t eventHubId,
+                                                                     const RawEvent* rawEvents,
+                                                                     size_t count) REQUIRES(mLock);
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
 
     void handleConfigurationChangedLocked(nsecs_t when) REQUIRES(mLock);
 
@@ -204,7 +202,8 @@
 
     void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock);
     void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock);
-    void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock);
+    [[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusStateLocked(const StylusState& state)
+            REQUIRES(mLock);
 
     // The PointerController that is shared among all the input devices that need it.
     std::weak_ptr<PointerControllerInterface> mPointerController;
@@ -231,6 +230,8 @@
     uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
     void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
 
+    void notifyAll(std::list<NotifyArgs>&& argsList);
+
     PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
 
     // state queries
@@ -246,5 +247,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_INPUT_READER_H
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 823d160..0beace1 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_READER_CONTEXT_H
-#define _UI_INPUTREADER_INPUT_READER_CONTEXT_H
+#pragma once
 
 #include <input/InputDevice.h>
+#include "NotifyArgs.h"
 
 #include <vector>
 
@@ -52,10 +52,10 @@
     virtual int32_t bumpGeneration() = 0;
 
     virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0;
-    virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
+    [[nodiscard]] virtual std::list<NotifyArgs> dispatchExternalStylusState(
+            const StylusState& outState) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
-    virtual InputListenerInterface& getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 
     virtual int32_t getNextId() = 0;
@@ -65,5 +65,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_INPUT_READER_CONTEXT_H
diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h
index 17f158c..8d14d3c 100644
--- a/services/inputflinger/reader/include/StylusState.h
+++ b/services/inputflinger/reader/include/StylusState.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_STYLUS_STATE_H
-#define _UI_INPUTREADER_STYLUS_STATE_H
+#pragma once
 
 #include <input/Input.h>
 
@@ -49,5 +48,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_STYLUS_STATE_H
diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h
index 7de9b830..08eba31 100644
--- a/services/inputflinger/reader/include/TouchVideoDevice.h
+++ b/services/inputflinger/reader/include/TouchVideoDevice.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
-#define _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
+#pragma once
 
 #include <android-base/unique_fd.h>
 #include <input/TouchVideoFrame.h>
@@ -123,5 +122,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 6058a1e..c691ca9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -25,6 +25,8 @@
 #include "PointerControllerInterface.h"
 #include "TouchCursorInputMapperCommon.h"
 
+#include "input/PrintTools.h"
+
 namespace android {
 
 // The default velocity control parameters that has no effect.
@@ -117,15 +119,17 @@
                          toString(mCursorScrollAccumulator.haveRelativeHWheel()));
     dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
     dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
+    dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
     dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
     dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
     dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
     dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
 }
 
-void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                  uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> CursorInputMapper::configure(nsecs_t when,
+                                                   const InputReaderConfiguration* config,
+                                                   uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
         mCursorScrollAccumulator.configure(getDeviceContext());
@@ -184,8 +188,7 @@
         }
         bumpGeneration();
         if (changes) {
-            NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
-            getListener().notifyDeviceReset(&args);
+            out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
         }
     }
 
@@ -205,26 +208,40 @@
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
         configurePointerCapture) {
+        const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
+
+        mDisplayId = ADISPLAY_ID_NONE;
+        if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+            // This InputDevice is associated with a viewport.
+            // Only generate events for the associated display.
+            const bool mismatchedPointerDisplay =
+                    isPointer && (viewport->displayId != mPointerController->getDisplayId());
+            mDisplayId = mismatchedPointerDisplay ? std::nullopt
+                                                  : std::make_optional(viewport->displayId);
+        } else if (isPointer) {
+            // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+            mDisplayId = mPointerController->getDisplayId();
+        }
+
         mOrientation = DISPLAY_ORIENTATION_0;
         const bool isOrientedDevice =
                 (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
-
         // 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. When pointer capture is enabled, we do not apply any
         // rotations and report values directly from the input device.
-        if (!isOrientedDevice && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
-            std::optional<DisplayViewport> internalViewport =
-                    config->getDisplayViewportByType(ViewportType::INTERNAL);
-            if (internalViewport) {
-                mOrientation = getInverseRotation(internalViewport->orientation);
+        if (!isOrientedDevice && mDisplayId &&
+            mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
+            if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) {
+                mOrientation = getInverseRotation(viewport->orientation);
             }
         }
 
         bumpGeneration();
     }
+    return out;
 }
 
 void CursorInputMapper::configureParameters() {
@@ -256,7 +273,7 @@
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
 }
 
-void CursorInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> CursorInputMapper::reset(nsecs_t when) {
     mButtonState = 0;
     mDownTime = 0;
 
@@ -268,20 +285,28 @@
     mCursorMotionAccumulator.reset(getDeviceContext());
     mCursorScrollAccumulator.reset(getDeviceContext());
 
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
-void CursorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     mCursorButtonAccumulator.process(rawEvent);
     mCursorMotionAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when, rawEvent->readTime);
+        out += sync(rawEvent->when, rawEvent->readTime);
     }
+    return out;
 }
 
-void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
+    if (!mDisplayId) {
+        // Ignore events when there is no target display configured.
+        return out;
+    }
+
     int32_t lastButtonState = mButtonState;
     int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
     mButtonState = currentButtonState;
@@ -327,7 +352,6 @@
 
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
-    int32_t displayId = ADISPLAY_ID_NONE;
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mSource == AINPUT_SOURCE_MOUSE) {
@@ -351,7 +375,6 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
-        displayId = mPointerController->getDisplayId();
     } else {
         // Pointer capture and navigation modes
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
@@ -372,8 +395,9 @@
     }
 
     // Synthesize key down from buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
-                         mSource, displayId, policyFlags, lastButtonState, currentButtonState);
+    out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+                                mSource, *mDisplayId, policyFlags, lastButtonState,
+                                currentButtonState);
 
     // Send motion event.
     if (downChanged || moved || scrolled || buttonsChanged) {
@@ -393,40 +417,38 @@
             while (!released.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
-                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
-                                             getDeviceId(), mSource, displayId, policyFlags,
-                                             AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
-                                             metaState, buttonState, MotionClassification::NONE,
-                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                             &pointerCoords, mXPrecision, mYPrecision,
-                                             xCursorPosition, yCursorPosition, downTime,
-                                             /* videoFrames */ {});
-                getListener().notifyMotion(&releaseArgs);
+                out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime,
+                                               getDeviceId(), mSource, *mDisplayId, policyFlags,
+                                               AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+                                               metaState, buttonState, MotionClassification::NONE,
+                                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                               &pointerCoords, mXPrecision, mYPrecision,
+                                               xCursorPosition, yCursorPosition, downTime,
+                                               /* videoFrames */ {}));
             }
         }
 
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, motionEventAction, 0, 0, metaState,
-                              currentButtonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
-                              mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
-                              /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, *mDisplayId, policyFlags, motionEventAction, 0, 0,
+                                       metaState, currentButtonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                       yCursorPosition, downTime,
+                                       /* videoFrames */ {}));
 
         if (buttonsPressed) {
             BitSet32 pressed(buttonsPressed);
             while (!pressed.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
-                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                           mSource, displayId, policyFlags,
-                                           AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
-                                           metaState, buttonState, MotionClassification::NONE,
-                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                           &pointerCoords, mXPrecision, mYPrecision,
-                                           xCursorPosition, yCursorPosition, downTime,
-                                           /* videoFrames */ {});
-                getListener().notifyMotion(&pressArgs);
+                out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime,
+                                               getDeviceId(), mSource, *mDisplayId, policyFlags,
+                                               AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
+                                               metaState, buttonState, MotionClassification::NONE,
+                                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                               &pointerCoords, mXPrecision, mYPrecision,
+                                               xCursorPosition, yCursorPosition, downTime,
+                                               /* videoFrames */ {}));
             }
         }
 
@@ -434,14 +456,14 @@
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
-            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                       mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                                       currentButtonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
-                                       yCursorPosition, downTime, /* videoFrames */ {});
-            getListener().notifyMotion(&hoverArgs);
+            out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, *mDisplayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                                           currentButtonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                           &pointerCoords, mXPrecision, mYPrecision,
+                                           xCursorPosition, yCursorPosition, downTime,
+                                           /* videoFrames */ {}));
         }
 
         // Send scroll events.
@@ -449,23 +471,25 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                        mSource, displayId, policyFlags,
-                                        AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                        currentButtonState, MotionClassification::NONE,
-                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
-                                        yCursorPosition, downTime, /* videoFrames */ {});
-            getListener().notifyMotion(&scrollArgs);
+            out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, *mDisplayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+                                           currentButtonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                           &pointerCoords, mXPrecision, mYPrecision,
+                                           xCursorPosition, yCursorPosition, downTime,
+                                           /* videoFrames */ {}));
         }
     }
 
     // Synthesize key up from buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
-                         displayId, policyFlags, lastButtonState, currentButtonState);
+    out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(),
+                                mSource, *mDisplayId, policyFlags, lastButtonState,
+                                currentButtonState);
 
     mCursorMotionAccumulator.finishSync();
     mCursorScrollAccumulator.finishSync();
+    return out;
 }
 
 int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
@@ -477,16 +501,7 @@
 }
 
 std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
-    if (mParameters.hasAssociatedDisplay) {
-        if (mParameters.mode == Parameters::Mode::POINTER) {
-            return std::make_optional(mPointerController->getDisplayId());
-        } else {
-            // If the device is orientationAware and not a mouse,
-            // it expects to dispatch events to any display
-            return std::make_optional(ADISPLAY_ID_NONE);
-        }
-    }
-    return std::nullopt;
+    return mDisplayId;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 75aeffb..6a4275e 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
-#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
+#pragma once
 
 #include "CursorButtonAccumulator.h"
 #include "CursorScrollAccumulator.h"
@@ -59,10 +58,11 @@
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
 
@@ -111,6 +111,10 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
+    // The display that events generated by this mapper should target. This can be set to
+    // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
+    // std::nullopt), all events will be ignored.
+    std::optional<int32_t> mDisplayId;
     int32_t mOrientation;
 
     std::shared_ptr<PointerControllerInterface> mPointerController;
@@ -121,9 +125,7 @@
     void configureParameters();
     void dumpParameters(std::string& dump);
 
-    void sync(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 6b5d37f..0404c9a 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -44,28 +44,32 @@
     dumpStylusState(dump, mStylusState);
 }
 
-void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                          uint32_t changes) {
+std::list<NotifyArgs> ExternalStylusInputMapper::configure(nsecs_t when,
+                                                           const InputReaderConfiguration* config,
+                                                           uint32_t changes) {
     getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
     mTouchButtonAccumulator.configure(getDeviceContext());
+    return {};
 }
 
-void ExternalStylusInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) {
     mSingleTouchMotionAccumulator.reset(getDeviceContext());
     mTouchButtonAccumulator.reset(getDeviceContext());
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
-void ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     mSingleTouchMotionAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
+        out += sync(rawEvent->when);
     }
+    return out;
 }
 
-void ExternalStylusInputMapper::sync(nsecs_t when) {
+std::list<NotifyArgs> ExternalStylusInputMapper::sync(nsecs_t when) {
     mStylusState.clear();
 
     mStylusState.when = when;
@@ -86,7 +90,7 @@
 
     mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
 
-    getContext()->dispatchExternalStylusState(mStylusState);
+    return getContext()->dispatchExternalStylusState(mStylusState);
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 516aa51..b6c9055 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
-#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -30,13 +29,14 @@
     explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext);
     virtual ~ExternalStylusInputMapper() = default;
 
-    virtual uint32_t getSources() const override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    uint32_t getSources() const override;
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void dump(std::string& dump) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
@@ -45,9 +45,7 @@
 
     StylusState mStylusState;
 
-    void sync(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 75cebf3..844afe0 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -32,12 +32,18 @@
 
 void InputMapper::dump(std::string& dump) {}
 
-void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                            uint32_t changes) {}
+std::list<NotifyArgs> InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                             uint32_t changes) {
+    return {};
+}
 
-void InputMapper::reset(nsecs_t when) {}
+std::list<NotifyArgs> InputMapper::reset(nsecs_t when) {
+    return {};
+}
 
-void InputMapper::timeoutExpired(nsecs_t when) {}
+std::list<NotifyArgs> InputMapper::timeoutExpired(nsecs_t when) {
+    return {};
+}
 
 int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
     return AKEY_STATE_UNKNOWN;
@@ -60,9 +66,14 @@
     return false;
 }
 
-void InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {}
+std::list<NotifyArgs> InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
+                                           int32_t token) {
+    return {};
+}
 
-void InputMapper::cancelVibrate(int32_t token) {}
+std::list<NotifyArgs> InputMapper::cancelVibrate(int32_t token) {
+    return {};
+}
 
 bool InputMapper::isVibrating() {
     return false;
@@ -72,7 +83,9 @@
     return {};
 }
 
-void InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {}
+std::list<NotifyArgs> InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+    return {};
+}
 
 bool InputMapper::enableSensor(InputDeviceSensorType sensorType,
                                std::chrono::microseconds samplingPeriod,
@@ -92,7 +105,9 @@
     return false;
 }
 
-void InputMapper::updateExternalStylusState(const StylusState& state) {}
+std::list<NotifyArgs> InputMapper::updateExternalStylusState(const StylusState& state) {
+    return {};
+}
 
 status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
     return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo);
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 7858728..104305b 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_INPUT_MAPPER_H
-#define _UI_INPUTREADER_INPUT_MAPPER_H
+#pragma once
 
 #include "EventHub.h"
 #include "InputDevice.h"
 #include "InputListener.h"
 #include "InputReaderContext.h"
+#include "NotifyArgs.h"
 #include "StylusState.h"
 #include "VibrationElement.h"
 
@@ -49,15 +49,16 @@
     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(); }
 
     virtual uint32_t getSources() const = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent) = 0;
-    virtual void timeoutExpired(nsecs_t when);
+    [[nodiscard]] virtual std::list<NotifyArgs> configure(nsecs_t when,
+                                                          const InputReaderConfiguration* config,
+                                                          uint32_t changes);
+    [[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when);
+    [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0;
+    [[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when);
 
     virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
@@ -66,11 +67,12 @@
 
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
                                        uint8_t* outFlags);
-    virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
-    virtual void cancelVibrate(int32_t token);
+    [[nodiscard]] virtual std::list<NotifyArgs> vibrate(const VibrationSequence& sequence,
+                                                        ssize_t repeat, int32_t token);
+    [[nodiscard]] virtual std::list<NotifyArgs> cancelVibrate(int32_t token);
     virtual bool isVibrating();
     virtual std::vector<int32_t> getVibratorIds();
-    virtual void cancelTouch(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] virtual std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime);
     virtual bool enableSensor(InputDeviceSensorType sensorType,
                               std::chrono::microseconds samplingPeriod,
                               std::chrono::microseconds maxBatchReportLatency);
@@ -92,7 +94,7 @@
      */
     virtual bool updateMetaState(int32_t keyCode);
 
-    virtual void updateExternalStylusState(const StylusState& state);
+    [[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
 
     virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
     virtual void updateLedState(bool reset) {}
@@ -109,5 +111,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 7d30d0c..929bf18 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -103,9 +103,10 @@
     }
 }
 
-void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                    uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> JoystickInputMapper::configure(nsecs_t when,
+                                                     const InputReaderConfiguration* config,
+                                                     uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
         // Collect all axes.
@@ -145,12 +146,12 @@
         for (auto it = mAxes.begin(); it != mAxes.end(); /*increment it inside loop*/) {
             Axis& axis = it->second;
             if (axis.axisInfo.axis < 0) {
-                while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
+                while (nextGenericAxisId <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE &&
                        haveAxis(nextGenericAxisId)) {
                     nextGenericAxisId += 1;
                 }
 
-                if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
+                if (nextGenericAxisId <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) {
                     axis.axisInfo.axis = nextGenericAxisId;
                     nextGenericAxisId += 1;
                 } else {
@@ -164,6 +165,7 @@
             it++;
         }
     }
+    return out;
 }
 
 JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo,
@@ -246,17 +248,18 @@
     }
 }
 
-void JoystickInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> JoystickInputMapper::reset(nsecs_t when) {
     // Recenter all axes.
     for (std::pair<const int32_t, Axis>& pair : mAxes) {
         Axis& axis = pair.second;
         axis.resetValue();
     }
 
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
-void JoystickInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     switch (rawEvent->type) {
         case EV_ABS: {
             auto it = mAxes.find(rawEvent->code);
@@ -298,16 +301,18 @@
         case EV_SYN:
             switch (rawEvent->code) {
                 case SYN_REPORT:
-                    sync(rawEvent->when, rawEvent->readTime, false /*force*/);
+                    out += sync(rawEvent->when, rawEvent->readTime, false /*force*/);
                     break;
             }
             break;
     }
+    return out;
 }
 
-void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
+std::list<NotifyArgs> JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
+    std::list<NotifyArgs> out;
     if (!filterAxes(force)) {
-        return;
+        return out;
     }
 
     int32_t metaState = getContext()->getGlobalMetaState();
@@ -340,13 +345,14 @@
         displayId = getDeviceContext().getAssociatedViewport()->displayId;
     }
 
-    NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
-                          AINPUT_SOURCE_JOYSTICK, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE,
-                          0, 0, metaState, buttonState, MotionClassification::NONE,
-                          AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
-    getListener().notifyMotion(&args);
+    out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                   AINPUT_SOURCE_JOYSTICK, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                   &pointerProperties, &pointerCoords, 0, 0,
+                                   AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                   AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+    return out;
 }
 
 void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 307bf5b..72b8a52 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
-#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -29,10 +28,11 @@
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 private:
     struct Axis {
@@ -92,7 +92,7 @@
     // Axes indexed by raw ABS_* axis index.
     std::unordered_map<int32_t, Axis> mAxes;
 
-    void sync(nsecs_t when, nsecs_t readTime, bool force);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime, bool force);
 
     bool haveAxis(int32_t axisId);
     void pruneAxes(bool ignoreExplicitlyMappedAxes);
@@ -111,5 +111,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 6a406d2..8704d1b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -127,7 +127,6 @@
     dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation());
     dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
     dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
-    dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
 }
 
 std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
@@ -144,9 +143,10 @@
     return std::nullopt;
 }
 
-void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                    uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> KeyboardInputMapper::configure(nsecs_t when,
+                                                     const InputReaderConfiguration* config,
+                                                     uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
         // Configure basic parameters.
@@ -156,6 +156,7 @@
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         mViewport = findViewport(when, config);
     }
+    return out;
 }
 
 static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
@@ -195,18 +196,18 @@
     dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat));
 }
 
-void KeyboardInputMapper::reset(nsecs_t when) {
-    mMetaState = AMETA_NONE;
-    mDownTime = 0;
-    mKeyDowns.clear();
+std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) {
+    std::list<NotifyArgs> out = cancelAllDownKeys(when);
     mCurrentHidUsage = 0;
 
     resetLedState();
 
-    InputMapper::reset(when);
+    out += InputMapper::reset(when);
+    return out;
 }
 
-void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     switch (rawEvent->type) {
         case EV_KEY: {
             int32_t scanCode = rawEvent->code;
@@ -214,8 +215,8 @@
             mCurrentHidUsage = 0;
 
             if (isKeyboardOrGamepadKey(scanCode)) {
-                processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
-                           usageCode);
+                out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
+                                  scanCode, usageCode);
             }
             break;
         }
@@ -231,6 +232,7 @@
             }
         }
     }
+    return out;
 }
 
 bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
@@ -268,8 +270,9 @@
     return false;
 }
 
-void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
-                                     int32_t usageCode) {
+std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down,
+                                                      int32_t scanCode, int32_t usageCode) {
+    std::list<NotifyArgs> out;
     int32_t keyCode;
     int32_t keyMetaState;
     uint32_t policyFlags;
@@ -281,6 +284,7 @@
         policyFlags = 0;
     }
 
+    nsecs_t downTime = when;
     if (down) {
         // Rotate key codes according to orientation if needed.
         if (mParameters.orientationAware) {
@@ -292,36 +296,37 @@
         if (keyDownIndex >= 0) {
             // key repeat, be sure to use same keycode as before in case of rotation
             keyCode = mKeyDowns[keyDownIndex].keyCode;
+            downTime = mKeyDowns[keyDownIndex].downTime;
         } else {
             // key down
             if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
                 getContext()->shouldDropVirtualKey(when, keyCode, scanCode)) {
-                return;
+                return out;
             }
             if (policyFlags & POLICY_FLAG_GESTURE) {
-                getDeviceContext().cancelTouch(when, readTime);
+                out += getDeviceContext().cancelTouch(when, readTime);
             }
 
             KeyDown keyDown;
             keyDown.keyCode = keyCode;
             keyDown.scanCode = scanCode;
+            keyDown.downTime = when;
             mKeyDowns.push_back(keyDown);
         }
-
-        mDownTime = when;
     } else {
         // Remove key down.
         ssize_t keyDownIndex = findKeyDown(scanCode);
         if (keyDownIndex >= 0) {
             // key up, be sure to use same keycode as before in case of rotation
             keyCode = mKeyDowns[keyDownIndex].keyCode;
+            downTime = mKeyDowns[keyDownIndex].downTime;
             mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
         } else {
             // key was not actually down
             ALOGI("Dropping key up from device %s because the key was not down.  "
                   "keyCode=%d, scanCode=%d",
                   getDeviceName().c_str(), keyCode, scanCode);
-            return;
+            return out;
         }
     }
 
@@ -333,8 +338,6 @@
         keyMetaState = mMetaState;
     }
 
-    nsecs_t downTime = mDownTime;
-
     // Key down on external an keyboard should wake the device.
     // We don't do this for internal keyboards to prevent them from waking up in your pocket.
     // For internal keyboards and devices for which the default wake behavior is explicitly
@@ -350,11 +353,12 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                       getDisplayId(), policyFlags,
-                       down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
-    getListener().notifyKey(&args);
+    out.push_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                                getDisplayId(), policyFlags,
+                                down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+                                AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
+                                downTime));
+    return out;
 }
 
 ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
@@ -473,4 +477,20 @@
     return std::nullopt;
 }
 
+std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) {
+    std::list<NotifyArgs> out;
+    size_t n = mKeyDowns.size();
+    for (size_t i = 0; i < n; i++) {
+        out.push_back(NotifyKeyArgs(getContext()->getNextId(), when,
+                                    systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
+                                    getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP,
+                                    AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+                                    mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
+                                    mKeyDowns[i].downTime));
+    }
+    mKeyDowns.clear();
+    mMetaState = AMETA_NONE;
+    return out;
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 0a55def..8d72ee9 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
-#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -26,30 +25,32 @@
     KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
     virtual ~KeyboardInputMapper();
 
-    virtual uint32_t getSources() const override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    uint32_t getSources() const override;
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void dump(std::string& dump) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
-    virtual bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
-                                       uint8_t* outFlags) override;
-    virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
+    int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+    bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
+                               uint8_t* outFlags) override;
+    int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
 
-    virtual int32_t getMetaState() override;
-    virtual bool updateMetaState(int32_t keyCode) override;
-    virtual std::optional<int32_t> getAssociatedDisplayId() override;
-    virtual void updateLedState(bool reset);
+    int32_t getMetaState() override;
+    bool updateMetaState(int32_t keyCode) override;
+    std::optional<int32_t> getAssociatedDisplayId() override;
+    void updateLedState(bool reset) override;
 
 private:
     // The current viewport.
     std::optional<DisplayViewport> mViewport;
 
     struct KeyDown {
+        nsecs_t downTime;
         int32_t keyCode;
         int32_t scanCode;
     };
@@ -59,7 +60,6 @@
 
     std::vector<KeyDown> mKeyDowns; // keys that are down
     int32_t mMetaState;
-    nsecs_t mDownTime; // time of most recent key down
 
     int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
 
@@ -87,7 +87,8 @@
     bool isKeyboardOrGamepadKey(int32_t scanCode);
     bool isMediaKey(int32_t keyCode);
 
-    void processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode);
+    [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
+                                                   int32_t scanCode, int32_t usageCode);
 
     bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
 
@@ -98,8 +99,7 @@
     void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
     std::optional<DisplayViewport> findViewport(nsecs_t when,
                                                 const InputReaderConfiguration* config);
+    [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 8f5dc9b..1d53eab 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -38,13 +38,9 @@
                                             bool usingSlotsProtocol) {
     mUsingSlotsProtocol = usingSlotsProtocol;
     mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
-
     mSlots = std::vector<Slot>(slotCount);
-}
 
-void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
-    // Unfortunately there is no way to read the initial contents of the slots.
-    // So when we reset the accumulator, we must assume they are all zeroes.
+    mCurrentSlot = -1;
     if (mUsingSlotsProtocol) {
         // Query the driver for the current slot index and use it as the initial slot
         // before we start reading events from the device.  It is possible that the
@@ -56,22 +52,20 @@
         // This can cause the touch point to "jump", but at least there will be
         // no stuck touches.
         int32_t initialSlot;
-        status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
-        if (status) {
-            ALOGD("Could not retrieve current multitouch slot index.  status=%d", status);
-            initialSlot = -1;
+        if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+            status == OK) {
+            mCurrentSlot = initialSlot;
+        } else {
+            ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
         }
-        clearSlots(initialSlot);
-    } else {
-        clearSlots(-1);
     }
 }
 
-void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
+void MultiTouchMotionAccumulator::resetSlots() {
     for (Slot& slot : mSlots) {
         slot.clear();
     }
-    mCurrentSlot = initialSlot;
+    mCurrentSlot = -1;
 }
 
 void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
@@ -159,7 +153,7 @@
 
 void MultiTouchMotionAccumulator::finishSync() {
     if (!mUsingSlotsProtocol) {
-        clearSlots(-1);
+        resetSlots();
     }
 }
 
@@ -197,18 +191,21 @@
 
 MultiTouchInputMapper::~MultiTouchInputMapper() {}
 
-void MultiTouchInputMapper::reset(nsecs_t when) {
-    mMultiTouchMotionAccumulator.reset(getDeviceContext());
-
-    mPointerIdBits.clear();
-
-    TouchInputMapper::reset(when);
+std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
+    // The evdev multi-touch protocol does not allow userspace applications to query the initial or
+    // current state of the pointers at any time. This means if we clear our accumulated state when
+    // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
+    // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
+    // rebuilding the state from scratch, we work around this kernel API limitation by never
+    // fully clearing any state specific to the multi-touch protocol.
+    return TouchInputMapper::reset(when);
 }
 
-void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
-    TouchInputMapper::process(rawEvent);
+std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
 
     mMultiTouchMotionAccumulator.process(rawEvent);
+    return out;
 }
 
 std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index fe8af5d..047e62d 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
-#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
+#pragma once
 
 #include "TouchInputMapper.h"
 
@@ -69,7 +68,6 @@
     MultiTouchMotionAccumulator();
 
     void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
-    void reset(InputDeviceContext& deviceContext);
     void process(const RawEvent* rawEvent);
     void finishSync();
     bool hasStylus() const;
@@ -86,7 +84,7 @@
     bool mUsingSlotsProtocol;
     bool mHaveStylus;
 
-    void clearSlots(int32_t initialSlot);
+    void resetSlots();
     void warnIfNotInUse(const RawEvent& event, const Slot& slot);
 };
 
@@ -95,8 +93,8 @@
     explicit MultiTouchInputMapper(InputDeviceContext& deviceContext);
     ~MultiTouchInputMapper() override;
 
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 protected:
     void syncTouch(nsecs_t when, RawState* outState) override;
@@ -122,5 +120,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 05973f7..29a1bda 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -60,9 +60,10 @@
                          toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
 }
 
-void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                         uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> RotaryEncoderInputMapper::configure(nsecs_t when,
+                                                          const InputReaderConfiguration* config,
+                                                          uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
     if (!changes) {
         mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
     }
@@ -75,23 +76,27 @@
             mOrientation = DISPLAY_ORIENTATION_0;
         }
     }
+    return out;
 }
 
-void RotaryEncoderInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::reset(nsecs_t when) {
     mRotaryEncoderScrollAccumulator.reset(getDeviceContext());
 
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
-void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     mRotaryEncoderScrollAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when, rawEvent->readTime);
+        out += sync(rawEvent->when, rawEvent->readTime);
     }
+    return out;
 }
 
-void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
     PointerCoords pointerCoords;
     pointerCoords.clear();
 
@@ -121,16 +126,17 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
-        NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                    mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0,
-                                    0, metaState, /* buttonState */ 0, MotionClassification::NONE,
-                                    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);
+        out.push_back(
+                NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                                 displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+                                 metaState, /* buttonState */ 0, MotionClassification::NONE,
+                                 AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                 &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                 AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
     }
 
     mRotaryEncoderScrollAccumulator.finishSync();
+    return out;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 1859355..f4352e7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
-#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
+#pragma once
 
 #include "CursorScrollAccumulator.h"
 #include "InputMapper.h"
@@ -30,10 +29,11 @@
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 private:
     CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
@@ -42,9 +42,7 @@
     float mScalingFactor;
     int32_t mOrientation;
 
-    void sync(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 573f99c..d81022f 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -122,9 +122,10 @@
     }
 }
 
-void SensorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                  uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> SensorInputMapper::configure(nsecs_t when,
+                                                   const InputReaderConfiguration* config,
+                                                   uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
         mDeviceEnabled = true;
@@ -158,6 +159,7 @@
             }
         }
     }
+    return out;
 }
 
 SensorInputMapper::Axis SensorInputMapper::createAxis(const AxisInfo& axisInfo,
@@ -185,7 +187,7 @@
     return Axis(rawAxisInfo, axisInfo, scale, offset, min, max, flat, fuzz, resolution, filter);
 }
 
-void SensorInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> SensorInputMapper::reset(nsecs_t when) {
     // Recenter all axes.
     for (std::pair<const int32_t, Axis>& pair : mAxes) {
         Axis& axis = pair.second;
@@ -193,7 +195,7 @@
     }
     mHardwareTimestamp = 0;
     mPrevMscTime = 0;
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
 SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType,
@@ -256,7 +258,8 @@
     mPrevMscTime = static_cast<uint32_t>(mscTime);
 }
 
-void SensorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SensorInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     switch (rawEvent->type) {
         case EV_ABS: {
             auto it = mAxes.find(rawEvent->code);
@@ -274,7 +277,7 @@
                         Axis& axis = pair.second;
                         axis.currentValue = axis.newValue;
                     }
-                    sync(rawEvent->when, false /*force*/);
+                    out += sync(rawEvent->when, false /*force*/);
                     break;
             }
             break;
@@ -287,6 +290,7 @@
                     break;
             }
     }
+    return out;
 }
 
 bool SensorInputMapper::setSensorEnabled(InputDeviceSensorType sensorType, bool enabled) {
@@ -375,7 +379,8 @@
     }
 }
 
-void SensorInputMapper::sync(nsecs_t when, bool force) {
+std::list<NotifyArgs> SensorInputMapper::sync(nsecs_t when, bool force) {
+    std::list<NotifyArgs> out;
     for (auto& [sensorType, sensor] : mSensors) {
         // Skip if sensor not enabled
         if (!sensor.enabled) {
@@ -405,17 +410,17 @@
             // Convert to Android unit
             convertFromLinuxToAndroid(values, sensorType);
             // Notify dispatcher for sensor event
-            NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(),
-                                  AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy,
-                                  sensor.accuracy !=
-                                          sensor.sensorInfo.accuracy /* accuracyChanged */,
-                                  timestamp /* hwTimestamp */, values);
-
-            getListener().notifySensor(&args);
+            out.push_back(NotifySensorArgs(getContext()->getNextId(), when, getDeviceId(),
+                                           AINPUT_SOURCE_SENSOR, sensorType,
+                                           sensor.sensorInfo.accuracy,
+                                           sensor.accuracy !=
+                                                   sensor.sensorInfo.accuracy /* accuracyChanged */,
+                                           timestamp /* hwTimestamp */, values));
             sensor.lastSampleTimeNs = timestamp;
             sensor.accuracy = sensor.sensorInfo.accuracy;
         }
     }
+    return out;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 27a6177..457567b 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
-#define _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -31,9 +30,11 @@
     uint32_t getSources() const override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
     bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
                       std::chrono::microseconds maxBatchReportLatency) override;
     void disableSensor(InputDeviceSensorType sensorType) override;
@@ -117,7 +118,7 @@
     // Sensor list
     std::unordered_map<InputDeviceSensorType, Sensor> mSensors;
 
-    void sync(nsecs_t when, bool force);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, bool force);
 
     template <typename T>
     bool tryGetProperty(std::string keyName, T& outValue);
@@ -133,5 +134,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index 4fff9be..13ad224 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -23,16 +23,17 @@
 
 SingleTouchInputMapper::~SingleTouchInputMapper() {}
 
-void SingleTouchInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> SingleTouchInputMapper::reset(nsecs_t when) {
     mSingleTouchMotionAccumulator.reset(getDeviceContext());
 
-    TouchInputMapper::reset(when);
+    return TouchInputMapper::reset(when);
 }
 
-void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
-    TouchInputMapper::process(rawEvent);
+std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
 
     mSingleTouchMotionAccumulator.process(rawEvent);
+    return out;
 }
 
 void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 9cb3f67..662e6bc 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
-#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
+#pragma once
 
 #include "SingleTouchMotionAccumulator.h"
 #include "TouchInputMapper.h"
@@ -27,8 +26,8 @@
     explicit SingleTouchInputMapper(InputDeviceContext& deviceContext);
     ~SingleTouchInputMapper() override;
 
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 protected:
     void syncTouch(nsecs_t when, RawState* outState) override;
@@ -40,5 +39,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index ebb5de6..c9101ca 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -29,7 +29,8 @@
     return AINPUT_SOURCE_SWITCH;
 }
 
-void SwitchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     switch (rawEvent->type) {
         case EV_SW:
             processSwitch(rawEvent->code, rawEvent->value);
@@ -37,9 +38,10 @@
 
         case EV_SYN:
             if (rawEvent->code == SYN_REPORT) {
-                sync(rawEvent->when);
+                out += sync(rawEvent->when);
             }
     }
+    return out;
 }
 
 void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
@@ -53,15 +55,16 @@
     }
 }
 
-void SwitchInputMapper::sync(nsecs_t when) {
+std::list<NotifyArgs> SwitchInputMapper::sync(nsecs_t when) {
+    std::list<NotifyArgs> out;
     if (mUpdatedSwitchMask) {
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
-                              updatedSwitchValues, mUpdatedSwitchMask);
-        getListener().notifySwitch(&args);
+        out.push_back(NotifySwitchArgs(getContext()->getNextId(), when, 0 /*policyFlags*/,
+                                       updatedSwitchValues, mUpdatedSwitchMask));
 
         mUpdatedSwitchMask = 0;
     }
+    return out;
 }
 
 int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index 64b9aa2..06d6504 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
-#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -27,7 +26,7 @@
     virtual ~SwitchInputMapper();
 
     virtual uint32_t getSources() const override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
     virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
     virtual void dump(std::string& dump) override;
@@ -37,9 +36,7 @@
     uint32_t mUpdatedSwitchMask;
 
     void processSwitch(int32_t switchCode, int32_t switchValue);
-    void sync(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 31a3d2e..5a7ba9a 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
-#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
+#pragma once
 
 #include <input/DisplayViewport.h>
 #include <stdint.h>
@@ -72,32 +71,34 @@
              AMOTION_EVENT_BUTTON_TERTIARY);
 }
 
-static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when,
-                                nsecs_t readTime, int32_t deviceId, uint32_t source,
-                                int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
-                                int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKey(
+        InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+        int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+        int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+    std::list<NotifyArgs> out;
     if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
          (currentButtonState & buttonState)) ||
         (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
          !(currentButtonState & buttonState))) {
-        NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId,
-                           policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
-        context->getListener().notifyKey(&args);
+        out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
+                                    displayId, policyFlags, action, 0, keyCode, 0,
+                                    context->getGlobalMetaState(), when));
     }
+    return out;
 }
 
-static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when,
-                                 nsecs_t readTime, int32_t deviceId, uint32_t source,
-                                 int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
-                                 int32_t currentButtonState) {
-    synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
-                        lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK,
-                        AKEYCODE_BACK);
-    synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
-                        lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD,
-                        AKEYCODE_FORWARD);
+[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKeys(
+        InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+        int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+        int32_t lastButtonState, int32_t currentButtonState) {
+    std::list<NotifyArgs> out;
+    out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+                               policyFlags, lastButtonState, currentButtonState,
+                               AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
+    out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+                               policyFlags, lastButtonState, currentButtonState,
+                               AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
+    return out;
 }
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 17ee54f..18a73ea 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -189,73 +189,74 @@
 void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
-    if (mDeviceMode != DeviceMode::DISABLED) {
-        info->addMotionRange(mOrientedRanges.x);
-        info->addMotionRange(mOrientedRanges.y);
-        info->addMotionRange(mOrientedRanges.pressure);
-
-        if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
-            // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
-            //
-            // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative
-            // motion, i.e. the hardware dimensions, as the finger could move completely across the
-            // touchpad in one sample cycle.
-            const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
-            const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
-            info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat,
-                                 x.fuzz, x.resolution);
-            info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat,
-                                 y.fuzz, y.resolution);
-        }
-
-        if (mOrientedRanges.size) {
-            info->addMotionRange(*mOrientedRanges.size);
-        }
-
-        if (mOrientedRanges.touchMajor) {
-            info->addMotionRange(*mOrientedRanges.touchMajor);
-            info->addMotionRange(*mOrientedRanges.touchMinor);
-        }
-
-        if (mOrientedRanges.toolMajor) {
-            info->addMotionRange(*mOrientedRanges.toolMajor);
-            info->addMotionRange(*mOrientedRanges.toolMinor);
-        }
-
-        if (mOrientedRanges.orientation) {
-            info->addMotionRange(*mOrientedRanges.orientation);
-        }
-
-        if (mOrientedRanges.distance) {
-            info->addMotionRange(*mOrientedRanges.distance);
-        }
-
-        if (mOrientedRanges.tilt) {
-            info->addMotionRange(*mOrientedRanges.tilt);
-        }
-
-        if (mCursorScrollAccumulator.haveRelativeVWheel()) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
-                                 0.0f);
-        }
-        if (mCursorScrollAccumulator.haveRelativeHWheel()) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
-                                 0.0f);
-        }
-        if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
-            const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
-            const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
-            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
-                                 x.fuzz, x.resolution);
-            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat,
-                                 y.fuzz, y.resolution);
-            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat,
-                                 x.fuzz, x.resolution);
-            info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
-                                 y.fuzz, y.resolution);
-        }
-        info->setButtonUnderPad(mParameters.hasButtonUnderPad);
+    if (mDeviceMode == DeviceMode::DISABLED) {
+        return;
     }
+
+    info->addMotionRange(mOrientedRanges.x);
+    info->addMotionRange(mOrientedRanges.y);
+    info->addMotionRange(mOrientedRanges.pressure);
+
+    if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
+        // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
+        //
+        // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative
+        // motion, i.e. the hardware dimensions, as the finger could move completely across the
+        // touchpad in one sample cycle.
+        const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
+        const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
+        info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat, x.fuzz,
+                             x.resolution);
+        info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat, y.fuzz,
+                             y.resolution);
+    }
+
+    if (mOrientedRanges.size) {
+        info->addMotionRange(*mOrientedRanges.size);
+    }
+
+    if (mOrientedRanges.touchMajor) {
+        info->addMotionRange(*mOrientedRanges.touchMajor);
+        info->addMotionRange(*mOrientedRanges.touchMinor);
+    }
+
+    if (mOrientedRanges.toolMajor) {
+        info->addMotionRange(*mOrientedRanges.toolMajor);
+        info->addMotionRange(*mOrientedRanges.toolMinor);
+    }
+
+    if (mOrientedRanges.orientation) {
+        info->addMotionRange(*mOrientedRanges.orientation);
+    }
+
+    if (mOrientedRanges.distance) {
+        info->addMotionRange(*mOrientedRanges.distance);
+    }
+
+    if (mOrientedRanges.tilt) {
+        info->addMotionRange(*mOrientedRanges.tilt);
+    }
+
+    if (mCursorScrollAccumulator.haveRelativeVWheel()) {
+        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+    }
+    if (mCursorScrollAccumulator.haveRelativeHWheel()) {
+        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+    }
+    if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
+        const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
+        const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
+        info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, x.fuzz,
+                             x.resolution);
+        info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, y.fuzz,
+                             y.resolution);
+        info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, x.fuzz,
+                             x.resolution);
+        info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, y.fuzz,
+                             y.resolution);
+    }
+    info->setButtonUnderPad(mParameters.hasButtonUnderPad);
+    info->setSupportsUsi(mParameters.supportsUsi);
 }
 
 void TouchInputMapper::dump(std::string& dump) {
@@ -346,9 +347,10 @@
     }
 }
 
-void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                 uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> TouchInputMapper::configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     mConfig = *config;
 
@@ -394,15 +396,13 @@
     }
 
     if (changes && resetNeeded) {
-        // If the device needs to be reset, cancel any ongoing gestures and reset the state.
-        cancelTouch(when, when);
-        reset(when);
+        out += reset(when);
 
         // 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);
+        out.emplace_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
     }
+    return out;
 }
 
 void TouchInputMapper::resolveExternalStylusPresence() {
@@ -441,11 +441,6 @@
     } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
         // The device is a pointing device like a track pad.
         mParameters.deviceType = Parameters::DeviceType::POINTER;
-    } else if (getDeviceContext().hasRelativeAxis(REL_X) ||
-               getDeviceContext().hasRelativeAxis(REL_Y)) {
-        // The device is a cursor device with a touch pad attached.
-        // By default don't use the touch pad to move the pointer.
-        mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
     } else {
         // The device is a touch pad of unknown purpose.
         mParameters.deviceType = Parameters::DeviceType::POINTER;
@@ -458,8 +453,6 @@
                                                              deviceTypeString)) {
         if (deviceTypeString == "touchScreen") {
             mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
-        } else if (deviceTypeString == "touchPad") {
-            mParameters.deviceType = Parameters::DeviceType::TOUCH_PAD;
         } else if (deviceTypeString == "touchNavigation") {
             mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
         } else if (deviceTypeString == "pointer") {
@@ -513,6 +506,10 @@
     // up in your pocket but you can enable it using the input device configuration.
     mParameters.wake = getDeviceContext().isExternal();
     getDeviceContext().getConfiguration().tryGetProperty("touch.wake", mParameters.wake);
+
+    mParameters.supportsUsi = false;
+    getDeviceContext().getConfiguration().tryGetProperty("touch.supportsUsi",
+                                                         mParameters.supportsUsi);
 }
 
 void TouchInputMapper::dumpParameters(std::string& dump) {
@@ -529,6 +526,7 @@
                          mParameters.uniqueDisplayId.c_str());
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
     dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n";
+    dump += StringPrintf(INDENT4 "SupportsUsi: %s\n", toString(mParameters.supportsUsi));
 }
 
 void TouchInputMapper::configureRawPointerAxes() {
@@ -1445,7 +1443,10 @@
                                                                  mInputDeviceOrientation);
 }
 
-void TouchInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) {
+    std::list<NotifyArgs> out = cancelTouch(when, when);
+    updateTouchSpots();
+
     mCursorButtonAccumulator.reset(getDeviceContext());
     mCursorScrollAccumulator.reset(getDeviceContext());
     mTouchButtonAccumulator.reset(getDeviceContext());
@@ -1476,7 +1477,7 @@
         mPointerController->clearSpots();
     }
 
-    InputMapper::reset(when);
+    return out += InputMapper::reset(when);
 }
 
 void TouchInputMapper::resetExternalStylus() {
@@ -1491,17 +1492,24 @@
     mExternalStylusFusionTimeout = LLONG_MAX;
 }
 
-void TouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> TouchInputMapper::process(const RawEvent* rawEvent) {
     mCursorButtonAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
+    std::list<NotifyArgs> out;
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when, rawEvent->readTime);
+        out += sync(rawEvent->when, rawEvent->readTime);
     }
+    return out;
 }
 
-void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
+    if (mDeviceMode == DeviceMode::DISABLED) {
+        // Only save the last pending state when the device is disabled.
+        mRawStatesPending.clear();
+    }
     // Push a new state.
     mRawStatesPending.emplace_back();
 
@@ -1531,14 +1539,13 @@
         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);
-    }
+    ALOGD_IF(DEBUG_RAW_EVENTS,
+             "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() &&
@@ -1547,16 +1554,15 @@
               next.rawPointerData.hoveringIdBits.value);
     }
 
-    processRawTouches(false /*timeout*/);
+    out += processRawTouches(false /*timeout*/);
+    return out;
 }
 
-void TouchInputMapper::processRawTouches(bool timeout) {
+std::list<NotifyArgs> TouchInputMapper::processRawTouches(bool timeout) {
+    std::list<NotifyArgs> out;
     if (mDeviceMode == DeviceMode::DISABLED) {
-        // Drop all input if the device is disabled.
-        cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
-        mCurrentCookedState.clear();
-        updateTouchSpots();
-        return;
+        // Do not process raw event while the device is disabled.
+        return out;
     }
 
     // Drain any pending touch states. The invariant here is that the mCurrentRawState is always
@@ -1581,7 +1587,7 @@
             mCurrentRawState.when = mLastRawState.when;
             mCurrentRawState.readTime = mLastRawState.readTime;
         }
-        cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
+        out += cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
     }
     if (count != 0) {
         mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
@@ -1592,19 +1598,20 @@
             nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
             clearStylusDataPendingFlags();
             mCurrentRawState.copyFrom(mLastRawState);
-            if (DEBUG_STYLUS_FUSION) {
-                ALOGD("Timeout expired, synthesizing event with new stylus data");
-            }
+            ALOGD_IF(DEBUG_STYLUS_FUSION,
+                     "Timeout expired, synthesizing event with new stylus data");
             const nsecs_t readTime = when; // consider this synthetic event to be zero latency
-            cookAndDispatch(when, readTime);
+            out += cookAndDispatch(when, readTime);
         } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
             mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
             getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
         }
     }
+    return out;
 }
 
-void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
     // Always start with a clean state.
     mCurrentCookedState.clear();
 
@@ -1630,7 +1637,9 @@
 
     // Consume raw off-screen touches before cooking pointer data.
     // If touches are consumed, subsequent code will not receive any pointer data.
-    if (consumeRawTouches(when, readTime, policyFlags)) {
+    bool consumed;
+    out += consumeRawTouches(when, readTime, policyFlags, consumed /*byref*/);
+    if (consumed) {
         mCurrentRawState.rawPointerData.clear();
     }
 
@@ -1643,9 +1652,9 @@
     applyExternalStylusTouchState(when);
 
     // Synthesize key down from raw buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
-                         mSource, mViewport.displayId, policyFlags, mLastCookedState.buttonState,
-                         mCurrentCookedState.buttonState);
+    out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+                                mSource, mViewport.displayId, policyFlags,
+                                mLastCookedState.buttonState, mCurrentCookedState.buttonState);
 
     // Dispatch the touches either directly or by translation through a pointer on screen.
     if (mDeviceMode == DeviceMode::POINTER) {
@@ -1687,15 +1696,15 @@
             pointerUsage = PointerUsage::GESTURES;
         }
 
-        dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
+        out += dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
     } else {
         if (!mCurrentMotionAborted) {
             updateTouchSpots();
-            dispatchButtonRelease(when, readTime, policyFlags);
-            dispatchHoverExit(when, readTime, policyFlags);
-            dispatchTouches(when, readTime, policyFlags);
-            dispatchHoverEnterAndMove(when, readTime, policyFlags);
-            dispatchButtonPress(when, readTime, policyFlags);
+            out += dispatchButtonRelease(when, readTime, policyFlags);
+            out += dispatchHoverExit(when, readTime, policyFlags);
+            out += dispatchTouches(when, readTime, policyFlags);
+            out += dispatchHoverEnterAndMove(when, readTime, policyFlags);
+            out += dispatchButtonPress(when, readTime, policyFlags);
         }
 
         if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
@@ -1704,9 +1713,9 @@
     }
 
     // Synthesize key up from raw buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
-                         mViewport.displayId, policyFlags, mLastCookedState.buttonState,
-                         mCurrentCookedState.buttonState);
+    out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(),
+                                mSource, mViewport.displayId, policyFlags,
+                                mLastCookedState.buttonState, mCurrentCookedState.buttonState);
 
     // Clear some transient state.
     mCurrentRawState.rawVScroll = 0;
@@ -1715,6 +1724,7 @@
     // Copy current touch to last touch in preparation for the next cycle.
     mLastRawState.copyFrom(mCurrentRawState);
     mLastCookedState.copyFrom(mCurrentCookedState);
+    return out;
 }
 
 void TouchInputMapper::updateTouchSpots() {
@@ -1780,24 +1790,18 @@
             state.rawPointerData.pointerCount != 0;
     if (initialDown) {
         if (mExternalStylusState.pressure != 0.0f) {
-            if (DEBUG_STYLUS_FUSION) {
-                ALOGD("Have both stylus and touch data, beginning fusion");
-            }
+            ALOGD_IF(DEBUG_STYLUS_FUSION, "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.");
-            }
+            ALOGD_IF(DEBUG_STYLUS_FUSION, "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);
-            }
+            ALOGD_IF(DEBUG_STYLUS_FUSION,
+                     "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)",
+                     mExternalStylusFusionTimeout);
             getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
             return true;
         }
@@ -1805,57 +1809,63 @@
 
     // 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");
-        }
+        ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up");
         mExternalStylusId = -1;
     }
 
     return false;
 }
 
-void TouchInputMapper::timeoutExpired(nsecs_t when) {
+std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) {
+    std::list<NotifyArgs> out;
     if (mDeviceMode == DeviceMode::POINTER) {
         if (mPointerUsage == PointerUsage::GESTURES) {
             // Since this is a synthetic event, we can consider its latency to be zero
             const nsecs_t readTime = when;
-            dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
+            out += dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
         }
     } else if (mDeviceMode == DeviceMode::DIRECT) {
         if (mExternalStylusFusionTimeout < when) {
-            processRawTouches(true /*timeout*/);
+            out += processRawTouches(true /*timeout*/);
         } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
             getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
         }
     }
+    return out;
 }
 
-void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
+std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) {
+    std::list<NotifyArgs> out;
     mExternalStylusState.copyFrom(state);
     if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
         // We're either in the middle of a fused stream of data or we're waiting on data before
         // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
         // data.
         mExternalStylusDataPending = true;
-        processRawTouches(false /*timeout*/);
+        out += processRawTouches(false /*timeout*/);
     }
+    return out;
 }
 
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags, bool& outConsumed) {
+    outConsumed = false;
+    std::list<NotifyArgs> out;
     // Check for release of a virtual key.
     if (mCurrentVirtualKey.down) {
         if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
             // 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);
-                }
-                dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
-                                   AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+                ALOGD_IF(DEBUG_VIRTUAL_KEYS,
+                         "VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+                         mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+                out.push_back(dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
+                                                 AKEY_EVENT_FLAG_FROM_SYSTEM |
+                                                         AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY));
             }
-            return true;
+            outConsumed = true;
+            return out;
         }
 
         if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1865,7 +1875,8 @@
             const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
             if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
                 // Pointer is still within the space of the virtual key.
-                return true;
+                outConsumed = true;
+                return out;
             }
         }
 
@@ -1875,13 +1886,12 @@
         // 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);
-            }
-            dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
-                               AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
-                                       AKEY_EVENT_FLAG_CANCELED);
+            ALOGD_IF(DEBUG_VIRTUAL_KEYS, "VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+                     mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+            out.push_back(dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
+                                             AKEY_EVENT_FLAG_FROM_SYSTEM |
+                                                     AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
+                                                     AKEY_EVENT_FLAG_CANCELED));
         }
     }
 
@@ -1908,17 +1918,18 @@
                                                                virtualKey->scanCode);
 
                     if (!mCurrentVirtualKey.ignored) {
-                        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);
+                        ALOGD_IF(DEBUG_VIRTUAL_KEYS,
+                                 "VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+                                 mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+                        out.push_back(dispatchVirtualKey(when, readTime, policyFlags,
+                                                         AKEY_EVENT_ACTION_DOWN,
+                                                         AKEY_EVENT_FLAG_FROM_SYSTEM |
+                                                                 AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY));
                     }
                 }
             }
-            return true;
+            outConsumed = true;
+            return out;
         }
     }
 
@@ -1940,43 +1951,50 @@
         !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
         getContext()->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
     }
-    return false;
+    return out;
 }
 
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                          int32_t keyEventAction, int32_t keyEventFlags) {
+NotifyKeyArgs TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime,
+                                                   uint32_t policyFlags, int32_t keyEventAction,
+                                                   int32_t keyEventFlags) {
     int32_t keyCode = mCurrentVirtualKey.keyCode;
     int32_t scanCode = mCurrentVirtualKey.scanCode;
     nsecs_t downTime = mCurrentVirtualKey.downTime;
     int32_t metaState = getContext()->getGlobalMetaState();
     policyFlags |= POLICY_FLAG_VIRTUAL;
 
-    NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
-                       AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
-                       keyEventFlags, keyCode, scanCode, metaState, downTime);
-    getListener().notifyKey(&args);
+    return NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                         AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
+                         keyEventFlags, keyCode, scanCode, metaState, downTime);
 }
 
-void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime,
+                                                     uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     if (mCurrentMotionAborted) {
         // Current motion event was already aborted.
-        return;
+        return out;
     }
     BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
     if (!currentIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = mCurrentCookedState.buttonState;
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
-                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mCurrentCookedState.cookedPointerData.pointerProperties,
-                       mCurrentCookedState.cookedPointerData.pointerCoords,
-                       mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
+                                     metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                     mCurrentCookedState.cookedPointerData.pointerProperties,
+                                     mCurrentCookedState.cookedPointerData.pointerCoords,
+                                     mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits,
+                                     -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
         mCurrentMotionAborted = true;
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime,
+                                                        uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
     BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
     int32_t metaState = getContext()->getGlobalMetaState();
@@ -1986,12 +2004,14 @@
         if (!currentIdBits.isEmpty()) {
             // No pointer id changes so this is a move event.
             // The listener takes care of batching moves so we don't have to deal with that here.
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
-                           metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
-                           mCurrentCookedState.cookedPointerData.pointerCoords,
-                           mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+            out.push_back(
+                    dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE,
+                                   0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                   mCurrentCookedState.cookedPointerData.pointerProperties,
+                                   mCurrentCookedState.cookedPointerData.pointerCoords,
+                                   mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits,
+                                   -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                   MotionClassification::NONE));
         }
     } else {
         // There may be pointers going up and pointers going down and pointers moving
@@ -2021,12 +2041,16 @@
             if (isCanceled) {
                 ALOGI("Canceling pointer %d for the palm event was detected.", upId);
             }
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
-                           isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
-                           mLastCookedState.cookedPointerData.pointerProperties,
-                           mLastCookedState.cookedPointerData.pointerCoords,
-                           mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_POINTER_UP, 0,
+                                         isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState,
+                                         buttonState, 0,
+                                         mLastCookedState.cookedPointerData.pointerProperties,
+                                         mLastCookedState.cookedPointerData.pointerCoords,
+                                         mLastCookedState.cookedPointerData.idToIndex,
+                                         dispatchedIdBits, upId, mOrientedXPrecision,
+                                         mOrientedYPrecision, mDownTime,
+                                         MotionClassification::NONE));
             dispatchedIdBits.clearBit(upId);
             mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
         }
@@ -2036,12 +2060,14 @@
         // events, they do not generally handle them except when presented in a move event.
         if (moveNeeded && !moveIdBits.isEmpty()) {
             ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
-                           metaState, buttonState, 0,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
-                           mCurrentCookedState.cookedPointerData.pointerCoords,
-                           mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,
+                                         mCurrentCookedState.cookedPointerData.pointerProperties,
+                                         mCurrentCookedState.cookedPointerData.pointerCoords,
+                                         mCurrentCookedState.cookedPointerData.idToIndex,
+                                         dispatchedIdBits, -1, mOrientedXPrecision,
+                                         mOrientedYPrecision, mDownTime,
+                                         MotionClassification::NONE));
         }
 
         // Dispatch pointer down events using the new pointer locations.
@@ -2054,59 +2080,75 @@
                 mDownTime = when;
             }
 
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
-                           0, 0, metaState, buttonState, 0,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
-                           mCurrentCookedState.cookedPointerData.pointerCoords,
-                           mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
-                           downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+            out.push_back(
+                    dispatchMotion(when, readTime, policyFlags, mSource,
+                                   AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState,
+                                   0, mCurrentCookedState.cookedPointerData.pointerProperties,
+                                   mCurrentCookedState.cookedPointerData.pointerCoords,
+                                   mCurrentCookedState.cookedPointerData.idToIndex,
+                                   dispatchedIdBits, downId, mOrientedXPrecision,
+                                   mOrientedYPrecision, mDownTime, MotionClassification::NONE));
         }
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     if (mSentHoverEnter &&
         (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() ||
          !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
         int32_t metaState = getContext()->getGlobalMetaState();
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
-                       metaState, mLastCookedState.buttonState, 0,
-                       mLastCookedState.cookedPointerData.pointerProperties,
-                       mLastCookedState.cookedPointerData.pointerCoords,
-                       mLastCookedState.cookedPointerData.idToIndex,
-                       mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision,
-                       mOrientedYPrecision, mDownTime);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+                                     mLastCookedState.buttonState, 0,
+                                     mLastCookedState.cookedPointerData.pointerProperties,
+                                     mLastCookedState.cookedPointerData.pointerCoords,
+                                     mLastCookedState.cookedPointerData.idToIndex,
+                                     mLastCookedState.cookedPointerData.hoveringIdBits, -1,
+                                     mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
         mSentHoverEnter = false;
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
-                                                 uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+                                                                  uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() &&
         !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         if (!mSentHoverEnter) {
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
-                           0, 0, metaState, mCurrentRawState.buttonState, 0,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
-                           mCurrentCookedState.cookedPointerData.pointerCoords,
-                           mCurrentCookedState.cookedPointerData.idToIndex,
-                           mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
+                                         mCurrentRawState.buttonState, 0,
+                                         mCurrentCookedState.cookedPointerData.pointerProperties,
+                                         mCurrentCookedState.cookedPointerData.pointerCoords,
+                                         mCurrentCookedState.cookedPointerData.idToIndex,
+                                         mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+                                         mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                         MotionClassification::NONE));
             mSentHoverEnter = true;
         }
 
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                       metaState, mCurrentRawState.buttonState, 0,
-                       mCurrentCookedState.cookedPointerData.pointerProperties,
-                       mCurrentCookedState.cookedPointerData.pointerCoords,
-                       mCurrentCookedState.cookedPointerData.idToIndex,
-                       mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                                     mCurrentRawState.buttonState, 0,
+                                     mCurrentCookedState.cookedPointerData.pointerProperties,
+                                     mCurrentCookedState.cookedPointerData.pointerCoords,
+                                     mCurrentCookedState.cookedPointerData.idToIndex,
+                                     mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+                                     mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
     const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
     const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2114,16 +2156,21 @@
     while (!releasedButtons.isEmpty()) {
         int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
         buttonState &= ~actionButton;
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-                       actionButton, 0, metaState, buttonState, 0,
-                       mCurrentCookedState.cookedPointerData.pointerProperties,
-                       mCurrentCookedState.cookedPointerData.pointerCoords,
-                       mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+                                     metaState, buttonState, 0,
+                                     mCurrentCookedState.cookedPointerData.pointerProperties,
+                                     mCurrentCookedState.cookedPointerData.pointerCoords,
+                                     mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+                                     mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime,
+                                                            uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
     const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
     const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2131,13 +2178,16 @@
     while (!pressedButtons.isEmpty()) {
         int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
         buttonState |= actionButton;
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                       actionButton, 0, metaState, buttonState, 0,
-                       mCurrentCookedState.cookedPointerData.pointerProperties,
-                       mCurrentCookedState.cookedPointerData.pointerCoords,
-                       mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState,
+                                     buttonState, 0,
+                                     mCurrentCookedState.cookedPointerData.pointerProperties,
+                                     mCurrentCookedState.cookedPointerData.pointerCoords,
+                                     mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+                                     mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
     }
+    return out;
 }
 
 const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) {
@@ -2419,54 +2469,62 @@
     }
 }
 
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                            PointerUsage pointerUsage) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags,
+                                                             PointerUsage pointerUsage) {
+    std::list<NotifyArgs> out;
     if (pointerUsage != mPointerUsage) {
-        abortPointerUsage(when, readTime, policyFlags);
+        out += abortPointerUsage(when, readTime, policyFlags);
         mPointerUsage = pointerUsage;
     }
 
     switch (mPointerUsage) {
         case PointerUsage::GESTURES:
-            dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
+            out += dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
             break;
         case PointerUsage::STYLUS:
-            dispatchPointerStylus(when, readTime, policyFlags);
+            out += dispatchPointerStylus(when, readTime, policyFlags);
             break;
         case PointerUsage::MOUSE:
-            dispatchPointerMouse(when, readTime, policyFlags);
+            out += dispatchPointerMouse(when, readTime, policyFlags);
             break;
         case PointerUsage::NONE:
             break;
     }
+    return out;
 }
 
-void TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     switch (mPointerUsage) {
         case PointerUsage::GESTURES:
-            abortPointerGestures(when, readTime, policyFlags);
+            out += abortPointerGestures(when, readTime, policyFlags);
             break;
         case PointerUsage::STYLUS:
-            abortPointerStylus(when, readTime, policyFlags);
+            out += abortPointerStylus(when, readTime, policyFlags);
             break;
         case PointerUsage::MOUSE:
-            abortPointerMouse(when, readTime, policyFlags);
+            out += abortPointerMouse(when, readTime, policyFlags);
             break;
         case PointerUsage::NONE:
             break;
     }
 
     mPointerUsage = PointerUsage::NONE;
+    return out;
 }
 
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                               bool isTimeout) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime,
+                                                                uint32_t policyFlags,
+                                                                bool isTimeout) {
+    std::list<NotifyArgs> out;
     // Update current gesture coordinates.
     bool cancelPreviousGesture, finishPreviousGesture;
     bool sendEvents =
             preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
     if (!sendEvents) {
-        return;
+        return {};
     }
     if (finishPreviousGesture) {
         cancelPreviousGesture = false;
@@ -2523,6 +2581,10 @@
     // Send events!
     int32_t metaState = getContext()->getGlobalMetaState();
     int32_t buttonState = mCurrentCookedState.buttonState;
+    const MotionClassification classification =
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE
+            ? MotionClassification::TWO_FINGER_SWIPE
+            : MotionClassification::NONE;
 
     uint32_t flags = 0;
 
@@ -2559,11 +2621,15 @@
     BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
     if (!dispatchedGestureIdBits.isEmpty()) {
         if (cancelPreviousGesture) {
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0,
-                           flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                           mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
-                           mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                           mPointerGesture.downTime);
+            const uint32_t cancelFlags = flags | AMOTION_EVENT_FLAG_CANCELED;
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_CANCEL, 0, cancelFlags, metaState,
+                                         buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                         mPointerGesture.lastGestureProperties,
+                                         mPointerGesture.lastGestureCoords,
+                                         mPointerGesture.lastGestureIdToIndex,
+                                         dispatchedGestureIdBits, -1, 0, 0,
+                                         mPointerGesture.downTime, classification));
 
             dispatchedGestureIdBits.clear();
         } else {
@@ -2577,12 +2643,14 @@
             while (!upGestureIdBits.isEmpty()) {
                 uint32_t id = upGestureIdBits.clearFirstMarkedBit();
 
-                dispatchMotion(when, readTime, policyFlags, mSource,
-                               AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState,
-                               AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
-                               mPointerGesture.lastGestureCoords,
-                               mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
-                               0, mPointerGesture.downTime);
+                out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                             AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState,
+                                             buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                             mPointerGesture.lastGestureProperties,
+                                             mPointerGesture.lastGestureCoords,
+                                             mPointerGesture.lastGestureIdToIndex,
+                                             dispatchedGestureIdBits, id, 0, 0,
+                                             mPointerGesture.downTime, classification));
 
                 dispatchedGestureIdBits.clearBit(id);
             }
@@ -2591,12 +2659,13 @@
 
     // Send motion events for all pointers that moved.
     if (moveNeeded) {
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags,
-                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mPointerGesture.currentGestureProperties,
-                       mPointerGesture.currentGestureCoords,
-                       mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                       mPointerGesture.downTime);
+        out.push_back(
+                dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0,
+                               flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                               mPointerGesture.currentGestureProperties,
+                               mPointerGesture.currentGestureCoords,
+                               mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1,
+                               0, 0, mPointerGesture.downTime, classification));
     }
 
     // Send motion events for all pointers that went down.
@@ -2611,23 +2680,26 @@
                 mPointerGesture.downTime = when;
             }
 
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
-                           0, flags, metaState, buttonState, 0,
-                           mPointerGesture.currentGestureProperties,
-                           mPointerGesture.currentGestureCoords,
-                           mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
-                           0, mPointerGesture.downTime);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_POINTER_DOWN, 0, flags, metaState,
+                                         buttonState, 0, mPointerGesture.currentGestureProperties,
+                                         mPointerGesture.currentGestureCoords,
+                                         mPointerGesture.currentGestureIdToIndex,
+                                         dispatchedGestureIdBits, id, 0, 0,
+                                         mPointerGesture.downTime, classification));
         }
     }
 
     // Send motion events for hover.
     if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
-                       flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mPointerGesture.currentGestureProperties,
-                       mPointerGesture.currentGestureCoords,
-                       mPointerGesture.currentGestureIdToIndex,
-                       mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState,
+                                     buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                     mPointerGesture.currentGestureProperties,
+                                     mPointerGesture.currentGestureCoords,
+                                     mPointerGesture.currentGestureIdToIndex,
+                                     mPointerGesture.currentGestureIdBits, -1, 0, 0,
+                                     mPointerGesture.downTime, MotionClassification::NONE));
     } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) {
         // Synthesize a hover move event after all pointers go up to indicate that
         // the pointer is hovering again even if the user is not currently touching
@@ -2647,12 +2719,13 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
         const int32_t displayId = mPointerController->getDisplayId();
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags,
-                              metaState, buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
-                              0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState,
+                                       buttonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                       &pointerCoords, 0, 0, x, y, mPointerGesture.downTime,
+                                       /* videoFrames */ {}));
     }
 
     // Update state.
@@ -2671,18 +2744,28 @@
             mPointerGesture.lastGestureIdToIndex[id] = index;
         }
     }
+    return out;
 }
 
-void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags) {
+    const MotionClassification classification =
+            mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE
+            ? MotionClassification::TWO_FINGER_SWIPE
+            : MotionClassification::NONE;
+    std::list<NotifyArgs> out;
     // Cancel previously dispatches pointers.
     if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = mCurrentRawState.buttonState;
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
-                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
-                       mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
-                       0, 0, mPointerGesture.downTime);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
+                                     metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                     mPointerGesture.lastGestureProperties,
+                                     mPointerGesture.lastGestureCoords,
+                                     mPointerGesture.lastGestureIdToIndex,
+                                     mPointerGesture.lastGestureIdBits, -1, 0, 0,
+                                     mPointerGesture.downTime, classification));
     }
 
     // Reset the current pointer gesture.
@@ -2694,6 +2777,7 @@
         mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
         mPointerController->clearSpots();
     }
+    return out;
 }
 
 bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
@@ -2703,9 +2787,7 @@
 
     // Handle TAP timeout.
     if (isTimeout) {
-        if (DEBUG_GESTURES) {
-            ALOGD("Gestures: Processing timeout");
-        }
+        ALOGD_IF(DEBUG_GESTURES, "Gestures: Processing timeout");
 
         if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
             if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
@@ -2714,9 +2796,7 @@
                                                    mConfig.pointerGestureTapDragInterval);
             } else {
                 // The tap is finished.
-                if (DEBUG_GESTURES) {
-                    ALOGD("Gestures: TAP finished");
-                }
+                ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP finished");
                 *outFinishPreviousGesture = true;
 
                 mPointerGesture.activeGestureId = -1;
@@ -2737,17 +2817,18 @@
 
     // Update the velocity tracker.
     {
-        std::vector<VelocityTracker::Position> positions;
+        std::vector<float> positionsX;
+        std::vector<float> positionsY;
         for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
                     mCurrentRawState.rawPointerData.pointerForId(id);
-            float x = pointer.x * mPointerXMovementScale;
-            float y = pointer.y * mPointerYMovementScale;
-            positions.push_back({x, y});
+            positionsX.push_back(pointer.x * mPointerXMovementScale);
+            positionsY.push_back(pointer.y * mPointerYMovementScale);
         }
         mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
-                                                    positions);
+                                                    {{AMOTION_EVENT_AXIS_X, positionsX},
+                                                     {AMOTION_EVENT_AXIS_Y, positionsY}});
     }
 
     // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
@@ -2812,11 +2893,9 @@
     // 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);
-        }
+        ALOGD_IF(DEBUG_GESTURES, "Gestures: QUIET for next %0.3fms",
+                 (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) *
+                         0.000001f);
         if (mPointerGesture.lastGestureMode != PointerGesture::Mode::QUIET) {
             *outFinishPreviousGesture = true;
         }
@@ -2840,11 +2919,9 @@
         // 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);
-        }
+        ALOGD_IF(DEBUG_GESTURES,
+                 "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;
@@ -2858,9 +2935,12 @@
             float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
             for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
                 uint32_t id = idBits.clearFirstMarkedBit();
-                float vx, vy;
-                if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
-                    float speed = hypotf(vx, vy);
+                std::optional<float> vx =
+                        mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
+                std::optional<float> vy =
+                        mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
+                if (vx && vy) {
+                    float speed = hypotf(*vx, *vy);
                     if (speed > bestSpeed) {
                         bestId = id;
                         bestSpeed = speed;
@@ -2869,30 +2949,17 @@
             }
             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);
-                }
+                ALOGD_IF(DEBUG_GESTURES,
+                         "Gestures: BUTTON_CLICK_OR_DRAG switched pointers, bestId=%d, "
+                         "bestSpeed=%0.3f",
+                         bestId, bestSpeed);
             }
         }
 
-        float deltaX = 0, deltaY = 0;
         if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
-            const RawPointerData::Pointer& currentPointer =
-                    mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
-            const RawPointerData::Pointer& lastPointer =
-                    mLastRawState.rawPointerData.pointerForId(activeTouchId);
-            deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
-            deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
-
-            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.
-            mPointerController->move(deltaX, deltaY);
+            moveMousePointerFromPointerDelta(when, activeTouchId);
         } else {
             mPointerVelocityControl.reset();
         }
@@ -2928,9 +2995,7 @@
                 mPointerController->getPosition(&x, &y);
                 if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                     fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
-                    if (DEBUG_GESTURES) {
-                        ALOGD("Gestures: TAP");
-                    }
+                    ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP");
 
                     mPointerGesture.tapUpTime = when;
                     getContext()->requestTimeoutAtTime(when +
@@ -2956,10 +3021,8 @@
 
                     tapped = true;
                 } else {
-                    if (DEBUG_GESTURES) {
-                        ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
-                              y - mPointerGesture.tapY);
-                    }
+                    ALOGD_IF(DEBUG_GESTURES, "Gestures: Not a TAP, deltaX=%f, deltaY=%f",
+                             x - mPointerGesture.tapX, y - mPointerGesture.tapY);
                 }
             } else {
                 if (DEBUG_GESTURES) {
@@ -2976,9 +3039,7 @@
         mPointerVelocityControl.reset();
 
         if (!tapped) {
-            if (DEBUG_GESTURES) {
-                ALOGD("Gestures: NEUTRAL");
-            }
+            ALOGD_IF(DEBUG_GESTURES, "Gestures: NEUTRAL");
             mPointerGesture.activeGestureId = -1;
             mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
             mPointerGesture.currentGestureIdBits.clear();
@@ -2999,50 +3060,30 @@
                     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);
-                    }
+                    ALOGD_IF(DEBUG_GESTURES, "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);
-                }
+                ALOGD_IF(DEBUG_GESTURES, "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;
         }
 
-        float deltaX = 0, deltaY = 0;
         if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) {
-            const RawPointerData::Pointer& currentPointer =
-                    mCurrentRawState.rawPointerData.pointerForId(activeTouchId);
-            const RawPointerData::Pointer& lastPointer =
-                    mLastRawState.rawPointerData.pointerForId(activeTouchId);
-            deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
-            deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
-
-            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.
-            mPointerController->move(deltaX, deltaY);
+            moveMousePointerFromPointerDelta(when, activeTouchId);
         } else {
             mPointerVelocityControl.reset();
         }
 
         bool down;
         if (mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG) {
-            if (DEBUG_GESTURES) {
-                ALOGD("Gestures: TAP_DRAG");
-            }
+            ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP_DRAG");
             down = true;
         } else {
-            if (DEBUG_GESTURES) {
-                ALOGD("Gestures: HOVER");
-            }
+            ALOGD_IF(DEBUG_GESTURES, "Gestures: HOVER");
             if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER) {
                 *outFinishPreviousGesture = true;
             }
@@ -3096,13 +3137,12 @@
         } 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);
-            }
+            ALOGD_IF(DEBUG_GESTURES,
+                     "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.
@@ -3116,13 +3156,12 @@
             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);
-            }
+            ALOGD_IF(DEBUG_GESTURES,
+                     "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);
@@ -3180,10 +3219,9 @@
             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);
-                    }
+                    ALOGD_IF(DEBUG_GESTURES,
+                             "Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+                             currentFingerCount);
                     *outCancelPreviousGesture = true;
                     mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                 } else {
@@ -3199,11 +3237,9 @@
                     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);
-                        }
+                        ALOGD_IF(DEBUG_GESTURES,
+                                 "Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+                                 mutualDistance, mPointerGestureMaxSwipeWidth);
                         *outCancelPreviousGesture = true;
                         mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                     } else {
@@ -3228,25 +3264,23 @@
                             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);
-                                }
+                                ALOGD_IF(DEBUG_GESTURES,
+                                         "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);
-                                }
+                                ALOGD_IF(DEBUG_GESTURES,
+                                         "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;
                             }
@@ -3258,10 +3292,9 @@
             // 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);
-                }
+                ALOGD_IF(DEBUG_GESTURES,
+                         "Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+                         currentFingerCount);
                 *outCancelPreviousGesture = true;
                 mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
             }
@@ -3295,11 +3328,10 @@
         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);
-            }
+            ALOGD_IF(DEBUG_GESTURES,
+                     "Gestures: PRESS or SWIPE activeTouchId=%d, activeGestureId=%d, "
+                     "currentTouchPointerCount=%d",
+                     activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
             ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
 
             mPointerGesture.currentGestureIdBits.clear();
@@ -3316,11 +3348,10 @@
             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);
-            }
+            ALOGD_IF(DEBUG_GESTURES,
+                     "Gestures: FREEFORM activeTouchId=%d, activeGestureId=%d, "
+                     "currentTouchPointerCount=%d",
+                     activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
             ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
 
             mPointerGesture.currentGestureIdBits.clear();
@@ -3359,13 +3390,11 @@
                 }
             }
 
-            if (DEBUG_GESTURES) {
-                ALOGD("Gestures: FREEFORM follow up "
-                      "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
-                      "activeGestureId=%d",
-                      mappedTouchIdBits.value, usedGestureIdBits.value,
-                      mPointerGesture.activeGestureId);
-            }
+            ALOGD_IF(DEBUG_GESTURES,
+                     "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++) {
@@ -3374,18 +3403,14 @@
                 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);
-                    }
+                    ALOGD_IF(DEBUG_GESTURES,
+                             "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);
-                    }
+                    ALOGD_IF(DEBUG_GESTURES,
+                             "Gestures: FREEFORM existing mapping for touch id %d -> gesture id %d",
+                             touchId, gestureId);
                 }
                 mPointerGesture.currentGestureIdBits.markBit(gestureId);
                 mPointerGesture.currentGestureIdToIndex[gestureId] = i;
@@ -3414,10 +3439,8 @@
             if (mPointerGesture.activeGestureId < 0) {
                 mPointerGesture.activeGestureId =
                         mPointerGesture.currentGestureIdBits.firstMarkedBit();
-                if (DEBUG_GESTURES) {
-                    ALOGD("Gestures: FREEFORM new activeGestureId=%d",
-                          mPointerGesture.activeGestureId);
-                }
+                ALOGD_IF(DEBUG_GESTURES, "Gestures: FREEFORM new activeGestureId=%d",
+                         mPointerGesture.activeGestureId);
             }
         }
     }
@@ -3457,7 +3480,22 @@
     return true;
 }
 
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+void TouchInputMapper::moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId) {
+    const RawPointerData::Pointer& currentPointer =
+            mCurrentRawState.rawPointerData.pointerForId(pointerId);
+    const RawPointerData::Pointer& lastPointer =
+            mLastRawState.rawPointerData.pointerForId(pointerId);
+    float deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
+    float deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
+
+    rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
+    mPointerVelocityControl.move(when, &deltaX, &deltaY);
+
+    mPointerController->move(deltaX, deltaY);
+}
+
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
@@ -3486,35 +3524,24 @@
         hovering = false;
     }
 
-    dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
+    return dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
 }
 
-void TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
-    abortPointerSimple(when, readTime, policyFlags);
+std::list<NotifyArgs> TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime,
+                                                           uint32_t policyFlags) {
+    return abortPointerSimple(when, readTime, policyFlags);
 }
 
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
     bool down, hovering;
     if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
         uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit();
-        uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
-        float deltaX = 0, deltaY = 0;
         if (mLastCookedState.mouseIdBits.hasBit(id)) {
-            uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id];
-            deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x -
-                      mLastRawState.rawPointerData.pointers[lastIndex].x) *
-                    mPointerXMovementScale;
-            deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y -
-                      mLastRawState.rawPointerData.pointers[lastIndex].y) *
-                    mPointerYMovementScale;
-
-            rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
-            mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
-            mPointerController->move(deltaX, deltaY);
+            moveMousePointerFromPointerDelta(when, id);
         } else {
             mPointerVelocityControl.reset();
         }
@@ -3524,6 +3551,7 @@
 
         float x, y;
         mPointerController->getPosition(&x, &y);
+        uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
         mPointerSimple.currentCoords.copyFrom(
                 mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3540,17 +3568,22 @@
         hovering = false;
     }
 
-    dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
+    return dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
 }
 
-void TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
-    abortPointerSimple(when, readTime, policyFlags);
+std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags) {
+    std::list<NotifyArgs> out = abortPointerSimple(when, readTime, policyFlags);
 
     mPointerVelocityControl.reset();
+
+    return out;
 }
 
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                             bool down, bool hovering) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags, bool down,
+                                                              bool hovering) {
+    std::list<NotifyArgs> out;
     int32_t metaState = getContext()->getGlobalMetaState();
 
     if (down || hovering) {
@@ -3570,28 +3603,29 @@
         mPointerSimple.down = false;
 
         // Send up.
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
-                              mLastRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0,
+                                       0, metaState, mLastRawState.buttonState,
+                                       MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                       yCursorPosition, mPointerSimple.downTime,
+                                       /* videoFrames */ {}));
     }
 
     if (mPointerSimple.hovering && !hovering) {
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
-                              metaState, mLastRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+                                       mLastRawState.buttonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                       yCursorPosition, mPointerSimple.downTime,
+                                       /* videoFrames */ {}));
     }
 
     if (down) {
@@ -3600,25 +3634,26 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                                  displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
-                                  metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener().notifyMotion(&args);
+            out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, displayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState,
+                                           mCurrentRawState.buttonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                           &mPointerSimple.currentProperties,
+                                           &mPointerSimple.currentCoords, mOrientedXPrecision,
+                                           mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                           mPointerSimple.downTime, /* videoFrames */ {}));
         }
 
         // Send move.
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                              mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &mPointerSimple.currentCoords, mOrientedXPrecision,
-                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE,
+                                       0, 0, metaState, mCurrentRawState.buttonState,
+                                       MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.currentProperties,
+                                       &mPointerSimple.currentCoords, mOrientedXPrecision,
+                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                       mPointerSimple.downTime, /* videoFrames */ {}));
     }
 
     if (hovering) {
@@ -3626,25 +3661,26 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                                  displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
-                                  metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener().notifyMotion(&args);
+            out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, displayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
+                                           mCurrentRawState.buttonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                           &mPointerSimple.currentProperties,
+                                           &mPointerSimple.currentCoords, mOrientedXPrecision,
+                                           mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                           mPointerSimple.downTime, /* videoFrames */ {}));
         }
 
         // Send hover move.
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                              metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &mPointerSimple.currentCoords, mOrientedXPrecision,
-                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(
+                NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                                 displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+                                 metaState, mCurrentRawState.buttonState,
+                                 MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                 &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                 mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                 yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}));
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3659,14 +3695,14 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                              mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL,
+                                       0, 0, metaState, mCurrentRawState.buttonState,
+                                       MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.currentProperties, &pointerCoords,
+                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                       yCursorPosition, mPointerSimple.downTime,
+                                       /* videoFrames */ {}));
     }
 
     // Save state.
@@ -3676,22 +3712,23 @@
     } else {
         mPointerSimple.reset();
     }
+    return out;
 }
 
-void TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime,
+                                                           uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
-    dispatchPointerSimple(when, readTime, policyFlags, false, false);
+    return dispatchPointerSimple(when, readTime, policyFlags, false, false);
 }
 
-void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                      uint32_t source, int32_t action, int32_t actionButton,
-                                      int32_t flags, int32_t metaState, int32_t buttonState,
-                                      int32_t edgeFlags, const PointerProperties* properties,
-                                      const PointerCoords* coords, const uint32_t* idToIndex,
-                                      BitSet32 idBits, int32_t changedId, float xPrecision,
-                                      float yPrecision, nsecs_t downTime) {
+NotifyMotionArgs TouchInputMapper::dispatchMotion(
+        nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
+        int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+        int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords,
+        const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
+        float yPrecision, nsecs_t downTime, MotionClassification classification) {
     PointerCoords pointerCoords[MAX_POINTERS];
     PointerProperties pointerProperties[MAX_POINTERS];
     uint32_t pointerCount = 0;
@@ -3737,12 +3774,11 @@
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
                   [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);
+    return NotifyMotionArgs(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
+                            policyFlags, action, actionButton, flags, metaState, buttonState,
+                            classification, edgeFlags, pointerCount, pointerProperties,
+                            pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+                            downTime, std::move(frames));
 }
 
 bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
@@ -3775,9 +3811,11 @@
     return changed;
 }
 
-void TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
-    abortPointerUsage(when, readTime, 0 /*policyFlags*/);
-    abortTouches(when, readTime, 0 /* policyFlags*/);
+std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
+    out += abortPointerUsage(when, readTime, 0 /*policyFlags*/);
+    out += abortTouches(when, readTime, 0 /* policyFlags*/);
+    return out;
 }
 
 // Transform input device coordinates to display panel coordinates.
@@ -3827,12 +3865,11 @@
 
 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);
-        }
+        ALOGD_IF(DEBUG_VIRTUAL_KEYS,
+                 "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;
@@ -4003,11 +4040,10 @@
                                                      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);
-            }
+            ALOGD_IF(DEBUG_POINTER_ASSIGNMENT,
+                     "assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
+                     ", distance=%" PRIu64,
+                     lastPointerIndex, currentPointerIndex, id, heap[0].distance);
             break;
         }
     }
@@ -4022,10 +4058,9 @@
         current.rawPointerData.markIdBit(id,
                                          current.rawPointerData.isHovering(currentPointerIndex));
 
-        if (DEBUG_POINTER_ASSIGNMENT) {
-            ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex,
-                  id);
-        }
+        ALOGD_IF(DEBUG_POINTER_ASSIGNMENT,
+                 "assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex,
+                 id);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 3cb11aa..7b0327e 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
-#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+#pragma once
 
 #include <stdint.h>
 
@@ -141,18 +140,21 @@
     uint32_t getSources() const override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
     int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
     int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
     bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
                                uint8_t* outFlags) override;
 
-    void cancelTouch(nsecs_t when, nsecs_t readTime) override;
-    void timeoutExpired(nsecs_t when) override;
-    void updateExternalStylusState(const StylusState& state) override;
+    [[nodiscard]] std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) override;
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(
+            const StylusState& state) override;
     std::optional<int32_t> getAssociatedDisplayId() override;
 
 protected:
@@ -197,7 +199,6 @@
     struct Parameters {
         enum class DeviceType {
             TOUCH_SCREEN,
-            TOUCH_PAD,
             TOUCH_NAVIGATION,
             POINTER,
 
@@ -231,6 +232,9 @@
         GestureMode gestureMode;
 
         bool wake;
+
+        // Whether the device supports the Universal Stylus Initiative (USI) protocol for styluses.
+        bool supportsUsi;
     } mParameters;
 
     // Immutable calibration parameters in parsed form.
@@ -322,6 +326,8 @@
         int32_t rawVScroll;
         int32_t rawHScroll;
 
+        explicit inline RawState() { clear(); }
+
         void copyFrom(const RawState& other) {
             when = other.when;
             readTime = other.readTime;
@@ -728,42 +734,64 @@
     void initializeOrientedRanges();
     void initializeSizeRanges();
 
-    void sync(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 
-    bool consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void processRawTouches(bool timeout);
-    void cookAndDispatch(nsecs_t when, nsecs_t readTime);
-    void dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                            int32_t keyEventAction, int32_t keyEventFlags);
+    [[nodiscard]] std::list<NotifyArgs> consumeRawTouches(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags, bool& outConsumed);
+    [[nodiscard]] std::list<NotifyArgs> processRawTouches(bool timeout);
+    [[nodiscard]] std::list<NotifyArgs> cookAndDispatch(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] NotifyKeyArgs dispatchVirtualKey(nsecs_t when, nsecs_t readTime,
+                                                   uint32_t policyFlags, int32_t keyEventAction,
+                                                   int32_t keyEventFlags);
 
-    void dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchTouches(nsecs_t when, nsecs_t readTime,
+                                                        uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchHoverExit(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+                                                                  uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchButtonRelease(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchButtonPress(nsecs_t when, nsecs_t readTime,
+                                                            uint32_t policyFlags);
     const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
     void cookPointerData();
-    void abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> abortTouches(nsecs_t when, nsecs_t readTime,
+                                                     uint32_t policyFlags);
 
-    void dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                              PointerUsage pointerUsage);
-    void abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerUsage(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags,
+                                                             PointerUsage pointerUsage);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerUsage(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags);
 
-    void dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                 bool isTimeout);
-    void abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerGestures(nsecs_t when, nsecs_t readTime,
+                                                                uint32_t policyFlags,
+                                                                bool isTimeout);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerGestures(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags);
     bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
                                 bool* outFinishPreviousGesture, bool isTimeout);
 
-    void dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    // Moves the on-screen mouse pointer based on the movement of the pointer of the given ID
+    // between the last and current events. Uses a relative motion.
+    void moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId);
 
-    void dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerStylus(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerStylus(nsecs_t when, nsecs_t readTime,
+                                                           uint32_t policyFlags);
 
-    void dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, bool down,
-                               bool hovering);
-    void abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerMouse(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerMouse(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags);
+
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerSimple(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags, bool down,
+                                                              bool hovering);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime,
+                                                           uint32_t policyFlags);
 
     bool assignExternalStylusId(const RawState& state, bool timeout);
     void applyExternalStylusButtonState(nsecs_t when);
@@ -773,11 +801,12 @@
     // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
     // method will take care of setting the index and transmuting the action to DOWN or UP
     // it is the first / last pointer to go down / up.
-    void dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source,
-                        int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
-                        int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties,
-                        const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
-                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
+    [[nodiscard]] NotifyMotionArgs dispatchMotion(
+            nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
+            int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+            int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords,
+            const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
+            float yPrecision, nsecs_t downTime, MotionClassification classification);
 
     // Updates pointer coords and properties for pointers with specified ids that have moved.
     // Returns true if any of them changed.
@@ -802,5 +831,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 33db527..7645b12 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -35,16 +35,18 @@
     info->setVibrator(true);
 }
 
-void VibratorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent* rawEvent) {
     // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
+    return {};
 }
 
-void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
-                                  int32_t token) {
+std::list<NotifyArgs> 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);
     }
+    std::list<NotifyArgs> out;
 
     mVibrating = true;
     mSequence = sequence;
@@ -53,19 +55,22 @@
     mIndex = -1;
 
     // Request InputReader to notify InputManagerService for vibration started.
-    NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true);
-    getListener().notifyVibratorState(&args);
-    nextStep();
+    out.push_back(
+            NotifyVibratorStateArgs(getContext()->getNextId(), systemTime(), getDeviceId(), true));
+    out += nextStep();
+    return out;
 }
 
-void VibratorInputMapper::cancelVibrate(int32_t token) {
+std::list<NotifyArgs> VibratorInputMapper::cancelVibrate(int32_t token) {
     if (DEBUG_VIBRATOR) {
         ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
     }
+    std::list<NotifyArgs> out;
 
     if (mVibrating && mToken == token) {
-        stopVibrating();
+        out.push_back(stopVibrating());
     }
+    return out;
 }
 
 bool VibratorInputMapper::isVibrating() {
@@ -76,26 +81,29 @@
     return getDeviceContext().getVibratorIds();
 }
 
-void VibratorInputMapper::timeoutExpired(nsecs_t when) {
+std::list<NotifyArgs> VibratorInputMapper::timeoutExpired(nsecs_t when) {
+    std::list<NotifyArgs> out;
     if (mVibrating) {
         if (when >= mNextStepTime) {
-            nextStep();
+            out += nextStep();
         } else {
             getContext()->requestTimeoutAtTime(mNextStepTime);
         }
     }
+    return out;
 }
 
-void VibratorInputMapper::nextStep() {
+std::list<NotifyArgs> VibratorInputMapper::nextStep() {
     if (DEBUG_VIBRATOR) {
         ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
     }
+    std::list<NotifyArgs> out;
     mIndex += 1;
     if (size_t(mIndex) >= mSequence.pattern.size()) {
         if (mRepeat < 0) {
             // We are done.
-            stopVibrating();
-            return;
+            out.push_back(stopVibrating());
+            return out;
         }
         mIndex = mRepeat;
     }
@@ -122,9 +130,10 @@
     if (DEBUG_VIBRATOR) {
         ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
     }
+    return out;
 }
 
-void VibratorInputMapper::stopVibrating() {
+NotifyVibratorStateArgs VibratorInputMapper::stopVibrating() {
     mVibrating = false;
     if (DEBUG_VIBRATOR) {
         ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
@@ -132,8 +141,7 @@
     getDeviceContext().cancelVibrate();
 
     // Request InputReader to notify InputManagerService for vibration complete.
-    NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false);
-    getListener().notifyVibratorState(&args);
+    return NotifyVibratorStateArgs(getContext()->getNextId(), systemTime(), getDeviceId(), false);
 }
 
 void VibratorInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index d3c22b6..e98f63a 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
-#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
+#pragma once
 
 #include "InputMapper.h"
 
@@ -28,13 +27,14 @@
 
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
-    virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) override;
-    virtual void cancelVibrate(int32_t token) override;
+    [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
+                                                int32_t token) override;
+    [[nodiscard]] std::list<NotifyArgs> cancelVibrate(int32_t token) override;
     virtual bool isVibrating() override;
     virtual std::vector<int32_t> getVibratorIds() override;
-    virtual void timeoutExpired(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
     virtual void dump(std::string& dump) override;
 
 private:
@@ -45,10 +45,8 @@
     ssize_t mIndex;
     nsecs_t mNextStepTime;
 
-    void nextStep();
-    void stopVibrating();
+    [[nodiscard]] std::list<NotifyArgs> nextStep();
+    [[nodiscard]] NotifyVibratorStateArgs stopVibrating();
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index 9e15906..ed4c789 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
-#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -48,5 +47,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
index 1649559..ae1b7a3 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
-#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -56,5 +55,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
index 4c011f1..93056f0 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
-#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -53,5 +52,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 22ebb72..7b889be 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
-#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
+#pragma once
 
 #include <stdint.h>
 
@@ -62,5 +61,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
\ No newline at end of file
diff --git a/services/inputflinger/reporter/InputReporterInterface.h b/services/inputflinger/reporter/InputReporterInterface.h
index e5d3606..72a4aeb 100644
--- a/services/inputflinger/reporter/InputReporterInterface.h
+++ b/services/inputflinger/reporter/InputReporterInterface.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_REPORTER_INTERFACE_H
-#define _UI_INPUT_REPORTER_INTERFACE_H
+#pragma once
 
 #include <utils/RefBase.h>
 
@@ -49,5 +48,3 @@
 sp<InputReporterInterface> createInputReporter();
 
 } // namespace android
-
-#endif // _UI_INPUT_REPORTER_INTERFACE_H
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 6da2145..fcbb98f 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -40,12 +40,10 @@
         "BlockingQueue_test.cpp",
         "EventHub_test.cpp",
         "FocusResolver_test.cpp",
-        "IInputFlingerQuery.aidl",
         "InputProcessor_test.cpp",
         "InputProcessorConverter_test.cpp",
         "InputDispatcher_test.cpp",
         "InputReader_test.cpp",
-        "InputFlingerService_test.cpp",
         "LatencyTracker_test.cpp",
         "PreferStylusOverTouch_test.cpp",
         "TestInputListener.cpp",
@@ -60,6 +58,7 @@
     },
     static_libs: [
         "libc++fs",
+        "libgmock",
     ],
     require_root: true,
     test_suites: ["device-tests"],
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 6ef6e44..9380c71 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -99,8 +99,6 @@
 };
 
 std::vector<RawEvent> EventHubTest::getEvents(std::optional<size_t> expectedEvents) {
-    static constexpr size_t EVENT_BUFFER_SIZE = 256;
-    std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
     std::vector<RawEvent> events;
 
     while (true) {
@@ -108,12 +106,12 @@
         if (expectedEvents) {
             timeout = 2s;
         }
-        const size_t count =
-                mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size());
-        if (count == 0) {
+
+        std::vector<RawEvent> newEvents = mEventHub->getEvents(timeout.count());
+        if (newEvents.empty()) {
             break;
         }
-        events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count);
+        events.insert(events.end(), newEvents.begin(), newEvents.end());
         if (expectedEvents && events.size() >= *expectedEvents) {
             break;
         }
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
deleted file mode 100644
index 5aeb21f..0000000
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ /dev/null
@@ -1,27 +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.
- */
-
-import android.InputChannel;
-import android.gui.FocusRequest;
-import android.gui.WindowInfo;
-
-/** @hide */
-interface IInputFlingerQuery
-{
-    /* Test interfaces */
-    void getInputChannels(out InputChannel[] channels);
-    void resetInputManager();
-}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 7ee6950..985c9c5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -63,10 +63,14 @@
 static constexpr int32_t POINTER_1_UP =
         AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-// The default pid and uid for windows created by the test.
+// The default pid and uid for windows created on the primary display by the test.
 static constexpr int32_t WINDOW_PID = 999;
 static constexpr int32_t WINDOW_UID = 1001;
 
+// The default pid and uid for the windows created on the secondary display by the test.
+static constexpr int32_t SECONDARY_WINDOW_PID = 1010;
+static constexpr int32_t SECONDARY_WINDOW_UID = 1012;
+
 // The default policy flags to use for event injection by tests.
 static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
 
@@ -500,11 +504,6 @@
         verify(*mFilteredEvent);
         mFilteredEvent = nullptr;
     }
-
-    bool isPerDisplayTouchModeEnabled() {
-        // TODO(b/198499018): Make this a regular property once per display touch mode is enabled
-        return false;
-    }
 };
 
 // --- InputDispatcherTest ---
@@ -5461,7 +5460,6 @@
                 sp<FakeWindowHandle>::make(mApplication, mDispatcher,
                                            "Window without input channel", ADISPLAY_ID_DEFAULT,
                                            std::make_optional<sp<IBinder>>(nullptr) /*token*/);
-
         mNoInputWindow->setNoInputChannel(true);
         mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
         // It's perfectly valid for this window to not have an associated input channel
@@ -6806,44 +6804,67 @@
 class InputDispatcherTouchModeChangedTests : public InputDispatcherTest {
 protected:
     std::shared_ptr<FakeApplicationHandle> mApp;
+    std::shared_ptr<FakeApplicationHandle> mSecondaryApp;
     sp<FakeWindowHandle> mWindow;
     sp<FakeWindowHandle> mSecondWindow;
+    sp<FakeWindowHandle> mThirdWindow;
 
     void SetUp() override {
         InputDispatcherTest::SetUp();
 
         mApp = std::make_shared<FakeApplicationHandle>();
+        mSecondaryApp = std::make_shared<FakeApplicationHandle>();
         mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
         mWindow->setFocusable(true);
         setFocusedWindow(mWindow);
         mSecondWindow =
                 sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
         mSecondWindow->setFocusable(true);
+        mThirdWindow =
+                sp<FakeWindowHandle>::make(mSecondaryApp, mDispatcher,
+                                           "TestWindow3_SecondaryDisplay", SECOND_DISPLAY_ID);
+        mThirdWindow->setFocusable(true);
 
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
-        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}},
+                                      {SECOND_DISPLAY_ID, {mThirdWindow}}});
+        mThirdWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
         mWindow->consumeFocusEvent(true);
 
-        // Set initial touch mode to InputDispatcher::kDefaultInTouchMode.
+        // Set main display initial touch mode to InputDispatcher::kDefaultInTouchMode.
         if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, WINDOW_PID,
-                                        WINDOW_UID, true /*hasPermission*/, ADISPLAY_ID_DEFAULT)) {
+                                        WINDOW_UID, true /* hasPermission */,
+                                        ADISPLAY_ID_DEFAULT)) {
             mWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
             mSecondWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
+            mThirdWindow->assertNoEvents();
+        }
+
+        // Set secondary display initial touch mode to InputDispatcher::kDefaultInTouchMode.
+        if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, SECONDARY_WINDOW_PID,
+                                        SECONDARY_WINDOW_UID, true /* hasPermission */,
+                                        SECOND_DISPLAY_ID)) {
+            mWindow->assertNoEvents();
+            mSecondWindow->assertNoEvents();
+            mThirdWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
         }
     }
 
-    void changeAndVerifyTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) {
+    void changeAndVerifyTouchModeInMainDisplayOnly(bool inTouchMode, int32_t pid, int32_t uid,
+                                                   bool hasPermission) {
         ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission,
                                                 ADISPLAY_ID_DEFAULT));
         mWindow->consumeTouchModeEvent(inTouchMode);
         mSecondWindow->consumeTouchModeEvent(inTouchMode);
+        mThirdWindow->assertNoEvents();
     }
 };
 
 TEST_F(InputDispatcherTouchModeChangedTests, FocusedWindowCanChangeTouchMode) {
     const WindowInfo& windowInfo = *mWindow->getInfo();
-    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid,
-                             windowInfo.ownerUid, false /*hasPermission*/);
+    changeAndVerifyTouchModeInMainDisplayOnly(!InputDispatcher::kDefaultInTouchMode,
+                                              windowInfo.ownerPid, windowInfo.ownerUid,
+                                              false /*  hasPermission */);
 }
 
 TEST_F(InputDispatcherTouchModeChangedTests, NonFocusedWindowOwnerCannotChangeTouchMode) {
@@ -6863,8 +6884,8 @@
     int32_t ownerPid = windowInfo.ownerPid;
     int32_t ownerUid = windowInfo.ownerUid;
     mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
-    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, ownerPid, ownerUid,
-                             true /*hasPermission*/);
+    changeAndVerifyTouchModeInMainDisplayOnly(!InputDispatcher::kDefaultInTouchMode, ownerPid,
+                                              ownerUid, true /*hasPermission*/);
 }
 
 TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) {
@@ -6876,6 +6897,16 @@
     mSecondWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchOnSecondaryDisplayOnly) {
+    const WindowInfo& windowInfo = *mThirdWindow->getInfo();
+    ASSERT_TRUE(mDispatcher->setInTouchMode(!InputDispatcher::kDefaultInTouchMode,
+                                            windowInfo.ownerPid, windowInfo.ownerUid,
+                                            true /*hasPermission*/, SECOND_DISPLAY_ID));
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+    mThirdWindow->consumeTouchModeEvent(!InputDispatcher::kDefaultInTouchMode);
+}
+
 TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInteractedWindow) {
     // Interact with the window first.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
deleted file mode 100644
index 22ae894..0000000
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ /dev/null
@@ -1,245 +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 <BnInputFlingerQuery.h>
-#include <IInputFlingerQuery.h>
-
-#include <android/os/BnInputFlinger.h>
-#include <android/os/IInputFlinger.h>
-
-#include <binder/Binder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-
-#include <input/Input.h>
-#include <input/InputTransport.h>
-
-#include <gtest/gtest.h>
-#include <inttypes.h>
-#include <linux/uinput.h>
-#include <log/log.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <chrono>
-#include <thread>
-#include <unordered_map>
-
-#define TAG "InputFlingerServiceTest"
-
-using android::gui::FocusRequest;
-using android::os::BnInputFlinger;
-using android::os::IInputFlinger;
-
-using std::chrono_literals::operator""ms;
-using std::chrono_literals::operator""s;
-
-namespace android {
-
-static const String16 kTestServiceName = String16("InputFlingerService");
-static const String16 kQueryServiceName = String16("InputFlingerQueryService");
-
-// --- InputFlingerServiceTest ---
-class InputFlingerServiceTest : public testing::Test {
-public:
-    void SetUp() override;
-    void TearDown() override;
-
-protected:
-    void InitializeInputFlinger();
-
-    sp<IInputFlinger> mService;
-    sp<IInputFlingerQuery> mQuery;
-
-private:
-    std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
-    std::mutex mLock;
-};
-
-
-class TestInputManager : public BnInputFlinger {
-protected:
-    virtual ~TestInputManager(){};
-
-public:
-    TestInputManager(){};
-
-    binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
-
-    status_t dump(int fd, const Vector<String16>& args) override;
-
-    binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
-    binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
-    binder::Status setFocusedWindow(const FocusRequest&) override;
-
-    void reset();
-
-private:
-    mutable Mutex mLock;
-    std::vector<std::shared_ptr<InputChannel>> mInputChannels;
-};
-
-class TestInputQuery : public BnInputFlingerQuery {
-public:
-    TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
-    binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
-    binder::Status resetInputManager() override;
-
-private:
-    sp<android::TestInputManager> mManager;
-};
-
-binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
-    return mManager->getInputChannels(channels);
-}
-
-binder::Status TestInputQuery::resetInputManager() {
-    mManager->reset();
-    return binder::Status::ok();
-}
-
-binder::Status TestInputManager::createInputChannel(const std::string& name,
-                                                    InputChannel* outChannel) {
-    AutoMutex _l(mLock);
-    std::unique_ptr<InputChannel> serverChannel;
-    std::unique_ptr<InputChannel> clientChannel;
-    InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-
-    clientChannel->copyTo(*outChannel);
-
-    mInputChannels.emplace_back(std::move(serverChannel));
-
-    return binder::Status::ok();
-}
-
-binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
-    AutoMutex _l(mLock);
-
-    auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
-                           [&](std::shared_ptr<InputChannel>& c) {
-                               return c->getConnectionToken() == connectionToken;
-                           });
-    if (it != mInputChannels.end()) {
-        mInputChannels.erase(it);
-    }
-
-    return binder::Status::ok();
-}
-
-status_t TestInputManager::dump(int fd, const Vector<String16>& args) {
-    std::string dump;
-
-    dump += " InputFlinger dump\n";
-
-    ::write(fd, dump.c_str(), dump.size());
-    return NO_ERROR;
-}
-
-binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
-    channels->clear();
-    for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
-        channels->push_back(*channel);
-    }
-    return binder::Status::ok();
-}
-
-binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
-    return binder::Status::ok();
-}
-
-void TestInputManager::reset() {
-    mInputChannels.clear();
-}
-
-void InputFlingerServiceTest::SetUp() {
-    InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
-    InitializeInputFlinger();
-}
-
-void InputFlingerServiceTest::TearDown() {
-    mQuery->resetInputManager();
-}
-
-void InputFlingerServiceTest::InitializeInputFlinger() {
-    sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
-    ASSERT_TRUE(input != nullptr);
-    mService = interface_cast<IInputFlinger>(input);
-
-    input = defaultServiceManager()->waitForService(kQueryServiceName);
-    ASSERT_TRUE(input != nullptr);
-    mQuery = interface_cast<IInputFlingerQuery>(input);
-}
-
-/**
- *  Test InputFlinger service interface createInputChannel
- */
-TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) {
-    // Test that the unblocked file descriptor flag is kept across processes over binder
-    // transactions.
-
-    InputChannel channel;
-    ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
-
-    const base::unique_fd& fd = channel.getFd();
-    ASSERT_TRUE(fd.ok());
-
-    const int result = fcntl(fd, F_GETFL);
-    EXPECT_NE(result, -1);
-    EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
-}
-
-TEST_F(InputFlingerServiceTest, CreateInputChannel) {
-    InputChannel channel;
-    ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
-
-    std::vector<::android::InputChannel> channels;
-    mQuery->getInputChannels(&channels);
-    ASSERT_EQ(channels.size(), 1UL);
-    EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken());
-
-    mService->removeInputChannel(channel.getConnectionToken());
-    mQuery->getInputChannels(&channels);
-    EXPECT_EQ(channels.size(), 0UL);
-}
-
-} // namespace android
-
-int main(int argc, char** argv) {
-    pid_t forkPid = fork();
-
-    if (forkPid == 0) {
-        // Server process
-        android::sp<android::TestInputManager> manager =
-                android::sp<android::TestInputManager>::make();
-        android::sp<android::TestInputQuery> query =
-                android::sp<android::TestInputQuery>::make(manager);
-
-        android::defaultServiceManager()->addService(android::kTestServiceName, manager,
-                                                     false /*allowIsolated*/);
-        android::defaultServiceManager()->addService(android::kQueryServiceName, query,
-                                                     false /*allowIsolated*/);
-        android::ProcessState::self()->startThreadPool();
-        android::IPCThreadState::self()->joinThreadPool();
-    } else {
-        android::ProcessState::self()->startThreadPool();
-        ::testing::InitGoogleTest(&argc, argv);
-        int result = RUN_ALL_TESTS();
-        kill(forkPid, SIGKILL);
-        return result;
-    }
-    return 0;
-}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index a0e6192..8ac8dfc 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -31,6 +31,7 @@
 #include <SingleTouchInputMapper.h>
 #include <SwitchInputMapper.h>
 #include <TestInputListener.h>
+#include <TestInputListenerMatchers.h>
 #include <TouchInputMapper.h>
 #include <UinputDevice.h>
 #include <VibratorInputMapper.h>
@@ -38,13 +39,16 @@
 #include <gtest/gtest.h>
 #include <gui/constants.h>
 
+#include "android/hardware/input/InputDeviceCountryCode.h"
 #include "input/DisplayViewport.h"
 #include "input/Input.h"
 
+using android::hardware::input::InputDeviceCountryCode;
+
 namespace android {
 
 using namespace ftl::flag_operators;
-
+using testing::AllOf;
 using std::chrono_literals::operator""ms;
 
 // Timeout for waiting for an expected event
@@ -56,7 +60,9 @@
 
 // Arbitrary display properties.
 static constexpr int32_t DISPLAY_ID = 0;
+static const std::string DISPLAY_UNIQUE_ID = "local:1";
 static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static const std::string SECONDARY_DISPLAY_UNIQUE_ID = "local:2";
 static constexpr int32_t DISPLAY_WIDTH = 480;
 static constexpr int32_t DISPLAY_HEIGHT = 800;
 static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
@@ -75,6 +81,7 @@
 static constexpr int32_t DEFAULT_BATTERY = 1;
 static constexpr int32_t BATTERY_STATUS = 4;
 static constexpr int32_t BATTERY_CAPACITY = 66;
+static const std::string BATTERY_DEVPATH = "/sys/devices/mydevice/power_supply/mybattery";
 static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000;
 static constexpr int32_t LIGHT_COLOR = 0x7F448866;
 static constexpr int32_t LIGHT_PLAYER_ID = 2;
@@ -455,6 +462,7 @@
         BitArray<MSC_MAX> mscBitmask;
         std::vector<VirtualKeyDefinition> virtualKeys;
         bool enabled;
+        InputDeviceCountryCode countryCode;
 
         status_t enable() {
             enabled = true;
@@ -509,7 +517,7 @@
         enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
     }
 
-    bool isDeviceEnabled(int32_t deviceId) {
+    bool isDeviceEnabled(int32_t deviceId) const override {
         Device* device = getDevice(deviceId);
         if (device == nullptr) {
             ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
@@ -518,7 +526,7 @@
         return device->enabled;
     }
 
-    status_t enableDevice(int32_t deviceId) {
+    status_t enableDevice(int32_t deviceId) override {
         status_t result;
         Device* device = getDevice(deviceId);
         if (device == nullptr) {
@@ -533,7 +541,7 @@
         return result;
     }
 
-    status_t disableDevice(int32_t deviceId) {
+    status_t disableDevice(int32_t deviceId) override {
         Device* device = getDevice(deviceId);
         if (device == nullptr) {
             ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
@@ -584,6 +592,11 @@
         device->keyCodeStates.replaceValueFor(keyCode, state);
     }
 
+    void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
+        Device* device = getDevice(deviceId);
+        device->countryCode = countryCode;
+    }
+
     void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
         Device* device = getDevice(deviceId);
         device->scanCodeStates.replaceValueFor(scanCode, state);
@@ -795,8 +808,8 @@
 
     status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
 
-    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
-                                                                      int32_t absCode) {
+    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+            int32_t deviceId, int32_t absCode) const override {
         Device* device = getDevice(deviceId);
         if (!device) {
             return Errorf("Sensor device not found.");
@@ -813,15 +826,14 @@
         mExcludedDevices = devices;
     }
 
-    size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override {
+    std::vector<RawEvent> getEvents(int) override {
         std::scoped_lock lock(mLock);
 
-        const size_t filledSize = std::min(mEvents.size(), bufferSize);
-        std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer);
+        std::vector<RawEvent> buffer;
+        std::swap(buffer, mEvents);
 
-        mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize);
         mEventsCondition.notify_all();
-        return filledSize;
+        return buffer;
     }
 
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
@@ -845,6 +857,14 @@
         return AKEY_STATE_UNKNOWN;
     }
 
+    InputDeviceCountryCode getCountryCode(int32_t deviceId) const override {
+        Device* device = getDevice(deviceId);
+        if (device) {
+            return device->countryCode;
+        }
+        return InputDeviceCountryCode::INVALID;
+    }
+
     int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
         Device* device = getDevice(deviceId);
         if (device) {
@@ -981,7 +1001,7 @@
 
     void cancelVibrate(int32_t) override {}
 
-    std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; };
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) const override { return mVibrators; };
 
     std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
         return BATTERY_CAPACITY;
@@ -991,13 +1011,21 @@
         return BATTERY_STATUS;
     }
 
-    const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; }
-
-    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
-        return std::nullopt;
+    std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override {
+        return {DEFAULT_BATTERY};
     }
 
-    const std::vector<int32_t> getRawLightIds(int32_t deviceId) override {
+    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+                                                    int32_t batteryId) const override {
+        if (batteryId != DEFAULT_BATTERY) return {};
+        static const auto BATTERY_INFO = RawBatteryInfo{.id = DEFAULT_BATTERY,
+                                                        .name = "default battery",
+                                                        .flags = InputBatteryClass::CAPACITY,
+                                                        .path = BATTERY_DEVPATH};
+        return BATTERY_INFO;
+    }
+
+    std::vector<int32_t> getRawLightIds(int32_t deviceId) const override {
         std::vector<int32_t> ids;
         for (const auto& [rawId, info] : mRawLightInfos) {
             ids.push_back(rawId);
@@ -1005,7 +1033,7 @@
         return ids;
     }
 
-    std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override {
+    std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override {
         auto it = mRawLightInfos.find(lightId);
         if (it == mRawLightInfos.end()) {
             return std::nullopt;
@@ -1022,7 +1050,7 @@
         mLightIntensities.emplace(lightId, intensities);
     };
 
-    std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override {
+    std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override {
         auto lightIt = mLightBrightness.find(lightId);
         if (lightIt == mLightBrightness.end()) {
             return std::nullopt;
@@ -1031,7 +1059,7 @@
     }
 
     std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
-            int32_t deviceId, int32_t lightId) override {
+            int32_t deviceId, int32_t lightId) const override {
         auto lightIt = mLightIntensities.find(lightId);
         if (lightIt == mLightIntensities.end()) {
             return std::nullopt;
@@ -1039,13 +1067,9 @@
         return lightIt->second;
     };
 
-    virtual bool isExternal(int32_t) const {
-        return false;
-    }
+    void dump(std::string&) const override {}
 
-    void dump(std::string&) override {}
-
-    void monitor() override {}
+    void monitor() const override {}
 
     void requestReopenDevices() override {}
 
@@ -1166,7 +1190,8 @@
         }
     }
 
-    void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override {
+    std::list<NotifyArgs> configure(nsecs_t, const InputReaderConfiguration* config,
+                                    uint32_t changes) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mConfigureWasCalled = true;
 
@@ -1177,19 +1202,22 @@
         }
 
         mStateChangedCondition.notify_all();
+        return {};
     }
 
-    void reset(nsecs_t) override {
+    std::list<NotifyArgs> reset(nsecs_t) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mResetWasCalled = true;
         mStateChangedCondition.notify_all();
+        return {};
     }
 
-    void process(const RawEvent* rawEvent) override {
+    std::list<NotifyArgs> process(const RawEvent* rawEvent) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mLastEvent = *rawEvent;
         mProcessWasCalled = true;
         mStateChangedCondition.notify_all();
+        return {};
     }
 
     int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
@@ -2144,6 +2172,8 @@
 
     ~FakePeripheralController() override {}
 
+    int32_t getEventHubId() const { return getDeviceContext().getEventHubId(); }
+
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {}
 
     void dump(std::string& dump) override {}
@@ -2177,6 +2207,7 @@
     InputDeviceContext& mDeviceContext;
     inline int32_t getDeviceId() { return mDeviceContext.getId(); }
     inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+    inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; }
 };
 
 TEST_F(InputReaderTest, BatteryGetCapacity) {
@@ -2213,6 +2244,21 @@
     ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
 }
 
+TEST_F(InputReaderTest, BatteryGetDevicePath) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    ftl::Flags<InputDeviceClass> deviceClass =
+            InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+    constexpr int32_t eventHubId = 1;
+    const char* DEVICE_LOCATION = "BLUETOOTH";
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    device->addController<FakePeripheralController>(eventHubId);
+    mReader->pushNextDevice(device);
+
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+    ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), BATTERY_DEVPATH);
+}
+
 TEST_F(InputReaderTest, LightGetColor) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     ftl::Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::LIGHT;
@@ -2688,6 +2734,17 @@
     ASSERT_EQ(ftl::Flags<InputDeviceClass>(0), mDevice->getClasses());
 }
 
+TEST_F(InputDeviceTest, CountryCodeCorrectlyMapped) {
+    mFakeEventHub->setCountryCode(EVENTHUB_ID, InputDeviceCountryCode::INTERNATIONAL);
+
+    // Configuration
+    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+    InputReaderConfiguration config;
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
+
+    ASSERT_EQ(InputDeviceCountryCode::INTERNATIONAL, mDevice->getDeviceInfo().getCountryCode());
+}
+
 TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) {
     ASSERT_EQ(mDevice->isEnabled(), false);
 }
@@ -2695,10 +2752,10 @@
 TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
     // Configuration.
     InputReaderConfiguration config;
-    mDevice->configure(ARBITRARY_TIME, &config, 0);
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
 
     // Reset.
-    mDevice->reset(ARBITRARY_TIME);
+    unused += mDevice->reset(ARBITRARY_TIME);
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -2754,7 +2811,7 @@
     mapper2.setMetaState(AMETA_SHIFT_ON);
 
     InputReaderConfiguration config;
-    mDevice->configure(ARBITRARY_TIME, &config, 0);
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
 
     std::string propertyValue;
     ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue))
@@ -2765,7 +2822,7 @@
     ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
 
     // Reset
-    mDevice->reset(ARBITRARY_TIME);
+    unused += mDevice->reset(ARBITRARY_TIME);
     ASSERT_NO_FATAL_FAILURE(mapper1.assertResetWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper2.assertResetWasCalled());
 
@@ -2821,7 +2878,7 @@
     // Event handling.
     RawEvent event;
     event.deviceId = EVENTHUB_ID;
-    mDevice->process(&event, 1);
+    unused += mDevice->process(&event, 1);
 
     ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
@@ -2834,7 +2891,8 @@
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
 
     // First Configuration.
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+    std::list<NotifyArgs> unused =
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
 
     // Device should be enabled by default.
     ASSERT_TRUE(mDevice->isEnabled());
@@ -2844,8 +2902,8 @@
     const std::string UNIQUE_ID = "local:1";
 
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     // Device should be disabled because it is associated with a specific display via
     // input port <-> display port association, but the corresponding display is not found
     ASSERT_FALSE(mDevice->isEnabled());
@@ -2854,19 +2912,19 @@
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
                                     ViewportType::INTERNAL);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled after set disable.
     mFakePolicy->addDisabledDevice(mDevice->getId());
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_ENABLED_STATE);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should still be disabled even found the associated display.
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 }
 
@@ -2874,52 +2932,67 @@
     // Device should be enabled by default.
     mFakePolicy->clearViewports();
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+    std::list<NotifyArgs> unused =
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled because it is associated with a specific display, but the
     // corresponding display is not found.
-    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should be enabled when a display is found.
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
                                     NO_PORT, ViewportType::INTERNAL);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled after set disable.
     mFakePolicy->addDisabledDevice(mDevice->getId());
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_ENABLED_STATE);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should still be disabled even found the associated display.
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 }
 
 TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) {
     mFakePolicy->clearViewports();
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+    std::list<NotifyArgs> unused =
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
 
-    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
                                     NO_PORT, ViewportType::INTERNAL);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
 }
 
+/**
+ * This test reproduces a crash caused by a dangling reference that remains after device is added
+ * and removed. The reference is accessed in InputDevice::dump(..);
+ */
+TEST_F(InputDeviceTest, DumpDoesNotCrash) {
+    constexpr int32_t TEST_EVENTHUB_ID = 10;
+    mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
+
+    InputDevice device(mReader->getContext(), 1 /*id*/, 2 /*generation*/, {} /*identifier*/);
+    device.addEventHubDevice(TEST_EVENTHUB_ID, true /*populateMappers*/);
+    device.removeEventHubDevice(TEST_EVENTHUB_ID);
+    std::string dumpStr, eventHubDevStr;
+    device.dump(dumpStr, eventHubDevStr);
+}
+
 // --- InputMapperTest ---
 
 class InputMapperTest : public testing::Test {
@@ -2962,7 +3035,7 @@
         mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
     }
 
-    void configureDevice(uint32_t changes) {
+    std::list<NotifyArgs> configureDevice(uint32_t changes) {
         if (!changes ||
             (changes &
              (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
@@ -2970,9 +3043,14 @@
             mReader->requestRefreshConfiguration(changes);
             mReader->loopOnce();
         }
-        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+        std::list<NotifyArgs> out =
+                mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
         // Loop the reader to flush the input listener queue.
+        for (const NotifyArgs& args : out) {
+            mFakeListener->notify(args);
+        }
         mReader->loopOnce();
+        return out;
     }
 
     std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
@@ -2994,9 +3072,12 @@
     T& addMapperAndConfigure(Args... args) {
         T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
         configureDevice(0);
-        mDevice->reset(ARBITRARY_TIME);
-        mapper.reset(ARBITRARY_TIME);
+        std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
+        resetArgList += mapper.reset(ARBITRARY_TIME);
         // Loop the reader to flush the input listener queue.
+        for (const NotifyArgs& loopArgs : resetArgList) {
+            mFakeListener->notify(loopArgs);
+        }
         mReader->loopOnce();
         return mapper;
     }
@@ -3013,8 +3094,8 @@
         mFakePolicy->clearViewports();
     }
 
-    void process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, int32_t code,
-                 int32_t value) {
+    std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
+                                  int32_t code, int32_t value) {
         RawEvent event;
         event.when = when;
         event.readTime = readTime;
@@ -3022,7 +3103,20 @@
         event.type = type;
         event.code = code;
         event.value = value;
-        mapper.process(&event);
+        std::list<NotifyArgs> processArgList = mapper.process(&event);
+        for (const NotifyArgs& args : processArgList) {
+            mFakeListener->notify(args);
+        }
+        // Loop the reader to flush the input listener queue.
+        mReader->loopOnce();
+        return processArgList;
+    }
+
+    void resetMapper(InputMapper& mapper, nsecs_t when) {
+        const auto resetArgs = mapper.reset(when);
+        for (const auto args : resetArgs) {
+            mFakeListener->notify(args);
+        }
         // Loop the reader to flush the input listener queue.
         mReader->loopOnce();
     }
@@ -3100,14 +3194,17 @@
 
 TEST_F(SwitchInputMapperTest, Process) {
     SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+    std::list<NotifyArgs> out;
+    out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
+    ASSERT_TRUE(out.empty());
+    out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+    ASSERT_TRUE(out.empty());
+    out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+    ASSERT_TRUE(out.empty());
+    out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
 
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-
-    NotifySwitchArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
+    ASSERT_EQ(1u, out.size());
+    const NotifySwitchArgs& args = std::get<NotifySwitchArgs>(*out.begin());
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues);
     ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
@@ -3154,22 +3251,23 @@
 
     ASSERT_FALSE(mapper.isVibrating());
     // Start vibrating
-    mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN);
+    std::list<NotifyArgs> out = mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN);
     ASSERT_TRUE(mapper.isVibrating());
     // Verify vibrator state listener was notified.
     mReader->loopOnce();
-    NotifyVibratorStateArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args));
-    ASSERT_EQ(DEVICE_ID, args.deviceId);
-    ASSERT_TRUE(args.isOn);
+    ASSERT_EQ(1u, out.size());
+    const NotifyVibratorStateArgs& vibrateArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
+    ASSERT_EQ(DEVICE_ID, vibrateArgs.deviceId);
+    ASSERT_TRUE(vibrateArgs.isOn);
     // Stop vibrating
-    mapper.cancelVibrate(VIBRATION_TOKEN);
+    out = mapper.cancelVibrate(VIBRATION_TOKEN);
     ASSERT_FALSE(mapper.isVibrating());
     // Verify vibrator state listener was notified.
     mReader->loopOnce();
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args));
-    ASSERT_EQ(DEVICE_ID, args.deviceId);
-    ASSERT_FALSE(args.isOn);
+    ASSERT_EQ(1u, out.size());
+    const NotifyVibratorStateArgs& cancelArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
+    ASSERT_EQ(DEVICE_ID, cancelArgs.deviceId);
+    ASSERT_FALSE(cancelArgs.isOn);
 }
 
 // --- SensorInputMapperTest ---
@@ -3825,7 +3923,7 @@
                                                        AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
 
     // Meta state should be AMETA_NONE after reset
-    mapper.reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
     // Meta state should be AMETA_NONE with update, as device doesn't have the keys.
     mapper.updateMetaState(AKEYCODE_NUM_LOCK);
@@ -3877,8 +3975,10 @@
     KeyboardInputMapper& mapper2 =
             device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
-    device2->reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused =
+            device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               0 /*changes*/);
+    unused += device2->reset(ARBITRARY_TIME);
 
     // Prepared displays and associated info.
     constexpr uint8_t hdmi1 = 0;
@@ -3889,8 +3989,8 @@
     mFakePolicy->addInputPortAssociation(USB2, hdmi2);
 
     // No associated display viewport found, should disable the device.
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_FALSE(device2->isEnabled());
 
     // Prepare second display.
@@ -3900,8 +4000,8 @@
     setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
                                  SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
     // Default device will reconfigure above, need additional reconfiguration for another device.
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 
     // Device should be enabled after the associated display is found.
     ASSERT_TRUE(mDevice->isEnabled());
@@ -3985,8 +4085,10 @@
     KeyboardInputMapper& mapper2 =
             device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
-    device2->reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused =
+            device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               0 /*changes*/);
+    unused += device2->reset(ARBITRARY_TIME);
 
     ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
@@ -4044,8 +4146,10 @@
     KeyboardInputMapper& mapper2 =
             device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
-    device2->reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused =
+            device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               0 /*changes*/);
+    unused += device2->reset(ARBITRARY_TIME);
 
     // Initial metastate is AMETA_NONE.
     ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
@@ -4091,6 +4195,37 @@
     ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
 }
 
+TEST_F(KeyboardInputMapperTest, Process_DisabledDevice) {
+    const int32_t USAGE_A = 0x070004;
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // Key down by scan code.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(DEVICE_ID, args.deviceId);
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+    ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+    ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+    ASSERT_EQ(KEY_HOME, args.scanCode);
+    ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+
+    // Disable device, it should synthesize cancellation events for down events.
+    mFakePolicy->addDisabledDevice(DEVICE_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+    ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+    ASSERT_EQ(KEY_HOME, args.scanCode);
+    ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags);
+}
+
 // --- KeyboardInputMapperTest_ExternalDevice ---
 
 class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
@@ -4194,10 +4329,14 @@
                             int32_t rotatedX, int32_t rotatedY);
 
     void prepareDisplay(int32_t orientation) {
-        const std::string uniqueId = "local:0";
-        const ViewportType viewportType = ViewportType::INTERNAL;
-        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                orientation, uniqueId, NO_PORT, viewportType);
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
+                                     DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+    }
+
+    void prepareSecondaryDisplay() {
+        setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                     DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
+                                     ViewportType::EXTERNAL);
     }
 
     static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y,
@@ -4474,6 +4613,7 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) {
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     addConfigurationProperty("cursor.mode", "navigation");
     // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
     // need to be rotated.
@@ -4492,11 +4632,13 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) {
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     addConfigurationProperty("cursor.mode", "navigation");
     // Since InputReader works in the un-rotated coordinate space, only devices that are not
     // orientation-aware are affected by display rotation.
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
@@ -4507,6 +4649,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
@@ -4517,6 +4660,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
@@ -4527,6 +4671,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
+    clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
@@ -5023,33 +5168,78 @@
     ASSERT_EQ(20, args.pointerCoords[0].getY());
 }
 
-TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    // Setup for second display.
-    constexpr int32_t SECOND_DISPLAY_ID = 1;
-    const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
-    mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
-                                    true /*isActive*/, SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
-                                    ViewportType::EXTERNAL);
-    mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
+    // Set up the default display.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+
+    // Set up the secondary display as the display on which the pointer should be shown.
+    // The InputDevice is not associated with any display.
+    prepareSecondaryDisplay();
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
     configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 
-    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     mFakePointerController->setPosition(100, 200);
     mFakePointerController->setButtonState(0);
 
-    NotifyMotionArgs args;
+    // Ensure input events are generated for the secondary display.
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
-    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
-    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+                  WithCoords(110.0f, 220.0f))));
     ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
-    ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+
+    // Set up the secondary display as the display on which the pointer should be shown,
+    // and associate the InputDevice with the secondary display.
+    prepareSecondaryDisplay();
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+                  WithCoords(110.0f, 220.0f))));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+}
+
+TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+    // Set up the default display as the display on which the pointer should be shown.
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+
+    // Associate the InputDevice with the secondary display.
+    prepareSecondaryDisplay();
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // The mapper should not generate any events because it is associated with a display that is
+    // different from the pointer display.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 // --- TouchInputMapperTest ---
@@ -5325,25 +5515,6 @@
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) {
-    mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_X);
-    mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_Y);
-    prepareButtons();
-    prepareAxes(POSITION);
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
-
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
-}
-
-TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) {
-    prepareButtons();
-    prepareAxes(POSITION);
-    addConfigurationProperty("touch.deviceType", "touchPad");
-    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
-
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
-}
-
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
     prepareButtons();
     prepareAxes(POSITION);
@@ -6702,6 +6873,55 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
+TEST_F(SingleTouchInputMapperTest, Reset_CancelsOngoingGesture) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION | PRESSURE);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    // Touch down.
+    processDown(mapper, 100, 200);
+    processPressure(mapper, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+    // Reset the mapper. This should cancel the ongoing gesture.
+    resetMapper(mapper, ARBITRARY_TIME);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION | PRESSURE);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    // Set the initial state for the touch pointer.
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 100);
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 200);
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MAX);
+    mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+
+    // Reset the mapper. When the mapper is reset, we expect it to attempt to recreate the touch
+    // state by reading the current axis values. Since there was no ongoing gesture, calling reset
+    // does not generate any events.
+    resetMapper(mapper, ARBITRARY_TIME);
+
+    // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+    // the recreated touch state to generate a down event.
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f))));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
 TEST_F(SingleTouchInputMapperTest,
        Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
@@ -6761,10 +6981,17 @@
     // No events are generated while the viewport is inactive.
     processMove(mapper, 101, 201);
     processSync(mapper);
-    processDown(mapper, 102, 202);
+    processUp(mapper);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
+    // Start a new gesture while the viewport is still inactive.
+    processDown(mapper, 300, 400);
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 300);
+    mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 400);
+    mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+    processSync(mapper);
+
     // Make the viewport active again. The device should resume processing events.
     viewport->isActive = true;
     mFakePolicy->updateViewport(*viewport);
@@ -6774,11 +7001,10 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
-    // Start a new gesture.
-    processDown(mapper, 100, 200);
+    // In the next sync, the touch state that was recreated when the device was reset is reported.
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
 
     // No more events.
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
@@ -8694,8 +8920,10 @@
 
     // Setup the second touch screen device.
     MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
-    device2->reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused =
+            device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               0 /*changes*/);
+    unused += device2->reset(ARBITRARY_TIME);
 
     // Setup PointerController.
     std::shared_ptr<FakePointerController> fakePointerController =
@@ -8714,9 +8942,9 @@
     prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
 
     // Default device will reconfigure above, need additional reconfiguration for another device.
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO |
-                               InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+    unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+                                         InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
 
     // Two fingers down at default display.
     int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -8746,8 +8974,8 @@
 
     // Disable the show touches configuration and ensure the spots are cleared.
     mFakePolicy->setShowTouches(false);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+    unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
 
     ASSERT_TRUE(fakePointerController->getSpots().empty());
 }
@@ -9310,6 +9538,75 @@
     ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
 }
 
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | PRESSURE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // First finger down.
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, 100, 200);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+    // Second finger down.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, 300, 400);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(
+            mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN)));
+
+    // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
+    // preserved. Resetting should cancel the ongoing gesture.
+    resetMapper(mapper, ARBITRARY_TIME);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
+
+    // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+    // the existing touch state to generate a down event.
+    processPosition(mapper, 301, 302);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(ACTION_POINTER_1_DOWN), WithPressure(1.f))));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | PRESSURE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // First finger touches down and releases.
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, 100, 200);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(
+            mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+    // Reset the mapper. When the mapper is reset, we expect it to restore the latest
+    // raw state where no pointers are down.
+    resetMapper(mapper, ARBITRARY_TIME);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Send an empty sync frame. Since there are no pointers, no events are generated.
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
 // --- MultiTouchInputMapperTest_ExternalDevice ---
 
 class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
@@ -9632,6 +9929,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
 
@@ -9653,6 +9951,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0,
                                                 movingDistance * mPointerMovementScale, 1, 0, 0, 0,
                                                 0, 0, 0, 0));
@@ -9690,6 +9989,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
 
@@ -9711,6 +10011,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
     // New coordinate is the scaled relative coordinate from the initial coordinate.
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0,
                                                 movingDistance * mPointerMovementScale, 1, 0, 0, 0,
@@ -9744,6 +10045,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     // One pointer for PRESS, and its coordinate is used as the origin for pointer coordinates.
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
@@ -9773,9 +10075,11 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_EQ(2U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN, motionArgs.action & AMOTION_EVENT_ACTION_MASK);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     // Two pointers' scaled relative coordinates from their initial centroid.
     // Initial y coordinates are 0 as y1 and y2 have the same value.
     float cookedX1 = (x1 - x2) / 2 * mPointerXZoomScale;
@@ -9805,6 +10109,7 @@
     ASSERT_EQ(2U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], cookedX1,
                                                 movingDistance * 2 * mPointerMovementScale, 1, 0, 0,
                                                 0, 0, 0, 0, 0));
@@ -9908,12 +10213,12 @@
         mFakePolicy.clear();
     }
 
-    void configureDevice(uint32_t changes) {
+    std::list<NotifyArgs> configureDevice(uint32_t changes) {
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
             mReader->requestRefreshConfiguration(changes);
             mReader->loopOnce();
         }
-        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+        return mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
     std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
@@ -9980,7 +10285,7 @@
 
 TEST_F(LightControllerTest, MonoLight) {
     RawLightInfo infoMono = {.id = 1,
-                             .name = "Mono",
+                             .name = "mono_light",
                              .maxBrightness = 255,
                              .flags = InputLightClass::BRIGHTNESS,
                              .path = ""};
@@ -9991,7 +10296,29 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::MONO, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS);
+}
+
+TEST_F(LightControllerTest, MonoKeyboardBacklight) {
+    RawLightInfo infoMono = {.id = 1,
+                             .name = "mono_keyboard_backlight",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS |
+                                     InputLightClass::KEYBOARD_BACKLIGHT,
+                             .path = ""};
+    mFakeEventHub->addRawLightInfo(infoMono.id, std::move(infoMono));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS);
@@ -10022,7 +10349,85 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::RGB, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, CorrectRGBKeyboardBacklight) {
+    RawLightInfo infoRed = {.id = 1,
+                            .name = "red_keyboard_backlight",
+                            .maxBrightness = 255,
+                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED |
+                                    InputLightClass::KEYBOARD_BACKLIGHT,
+                            .path = ""};
+    RawLightInfo infoGreen = {.id = 2,
+                              .name = "green_keyboard_backlight",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN |
+                                      InputLightClass::KEYBOARD_BACKLIGHT,
+                              .path = ""};
+    RawLightInfo infoBlue = {.id = 3,
+                             .name = "blue_keyboard_backlight",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE |
+                                     InputLightClass::KEYBOARD_BACKLIGHT,
+                             .path = ""};
+    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, IncorrectRGBKeyboardBacklight) {
+    RawLightInfo infoRed = {.id = 1,
+                            .name = "red",
+                            .maxBrightness = 255,
+                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
+                            .path = ""};
+    RawLightInfo infoGreen = {.id = 2,
+                              .name = "green",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
+                              .path = ""};
+    RawLightInfo infoBlue = {.id = 3,
+                             .name = "blue",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
+                             .path = ""};
+    RawLightInfo infoGlobal = {.id = 3,
+                               .name = "global_keyboard_backlight",
+                               .maxBrightness = 255,
+                               .flags = InputLightClass::BRIGHTNESS | InputLightClass::GLOBAL |
+                                       InputLightClass::KEYBOARD_BACKLIGHT,
+                               .path = ""};
+    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoGlobal));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
@@ -10030,7 +10435,7 @@
 
 TEST_F(LightControllerTest, MultiColorRGBLight) {
     RawLightInfo infoColor = {.id = 1,
-                              .name = "red",
+                              .name = "multi_color",
                               .maxBrightness = 255,
                               .flags = InputLightClass::BRIGHTNESS |
                                       InputLightClass::MULTI_INTENSITY |
@@ -10044,7 +10449,34 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, MultiColorRGBKeyboardBacklight) {
+    RawLightInfo infoColor = {.id = 1,
+                              .name = "multi_color_keyboard_backlight",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS |
+                                      InputLightClass::MULTI_INTENSITY |
+                                      InputLightClass::MULTI_INDEX |
+                                      InputLightClass::KEYBOARD_BACKLIGHT,
+                              .path = ""};
+
+    mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
@@ -10082,6 +10514,8 @@
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
     ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type);
+    ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_FALSE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_TRUE(controller.setLightPlayerId(lights[0].id, LIGHT_PLAYER_ID));
diff --git a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
index 8e2ab88..bd05360 100644
--- a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
+++ b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
@@ -33,14 +33,8 @@
 constexpr int32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
 constexpr int32_t STYLUS = AINPUT_SOURCE_STYLUS;
 
-struct PointerData {
-    float x;
-    float y;
-};
-
 static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action,
-                                           const std::vector<PointerData>& points,
-                                           uint32_t source) {
+                                           const std::vector<Point>& points, uint32_t source) {
     size_t pointerCount = points.size();
     if (action == DOWN || action == UP) {
         EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 6a26c63..29093ef 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -69,6 +69,13 @@
                                            "Expected notifyMotion() to have been called."));
 }
 
+void TestInputListener::assertNotifyMotionWasCalled(
+        const ::testing::Matcher<NotifyMotionArgs>& matcher) {
+    NotifyMotionArgs outEventArgs;
+    ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs));
+    ASSERT_THAT(outEventArgs, matcher);
+}
+
 void TestInputListener::assertNotifyMotionWasNotCalled() {
     ASSERT_NO_FATAL_FAILURE(
             assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called."));
@@ -140,7 +147,7 @@
 }
 
 template <class NotifyArgsType>
-void TestInputListener::notify(const NotifyArgsType* args) {
+void TestInputListener::addToQueue(const NotifyArgsType* args) {
     std::scoped_lock<std::mutex> lock(mLock);
 
     std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
@@ -149,35 +156,35 @@
 }
 
 void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-    notify<NotifyConfigurationChangedArgs>(args);
+    addToQueue<NotifyConfigurationChangedArgs>(args);
 }
 
 void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-    notify<NotifyDeviceResetArgs>(args);
+    addToQueue<NotifyDeviceResetArgs>(args);
 }
 
 void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
-    notify<NotifyKeyArgs>(args);
+    addToQueue<NotifyKeyArgs>(args);
 }
 
 void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
-    notify<NotifyMotionArgs>(args);
+    addToQueue<NotifyMotionArgs>(args);
 }
 
 void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
-    notify<NotifySwitchArgs>(args);
+    addToQueue<NotifySwitchArgs>(args);
 }
 
 void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
-    notify<NotifyPointerCaptureChangedArgs>(args);
+    addToQueue<NotifyPointerCaptureChangedArgs>(args);
 }
 
 void TestInputListener::notifySensor(const NotifySensorArgs* args) {
-    notify<NotifySensorArgs>(args);
+    addToQueue<NotifySensorArgs>(args);
 }
 
 void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
-    notify<NotifyVibratorStateArgs>(args);
+    addToQueue<NotifyVibratorStateArgs>(args);
 }
 
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 626cdfc..4ad1c42 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef _UI_TEST_INPUT_LISTENER_H
-#define _UI_TEST_INPUT_LISTENER_H
+#pragma once
 
 #include <android-base/thread_annotations.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include "InputListener.h"
 
@@ -48,6 +48,8 @@
 
     void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
 
+    void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher);
+
     void assertNotifyMotionWasNotCalled();
 
     void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
@@ -65,7 +67,7 @@
     void assertNotCalled(std::string message);
 
     template <class NotifyArgsType>
-    void notify(const NotifyArgsType* args);
+    void addToQueue(const NotifyArgsType* args);
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
 
@@ -100,4 +102,3 @@
 };
 
 } // namespace android
-#endif
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
new file mode 100644
index 0000000..ff7455b
--- /dev/null
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/input.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+MATCHER_P(WithMotionAction, action, "InputEvent with specified action") {
+    bool matches = action == arg.action;
+    if (!matches) {
+        *result_listener << "expected action " << MotionEvent::actionToString(action)
+                         << ", but got " << MotionEvent::actionToString(arg.action);
+    }
+    if (action == AMOTION_EVENT_ACTION_CANCEL) {
+        if (!matches) {
+            *result_listener << "; ";
+        }
+        *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
+        matches &= (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+    }
+    return matches;
+}
+
+MATCHER_P(WithSource, source, "InputEvent with specified source") {
+    *result_listener << "expected source " << source << ", but got " << arg.source;
+    return arg.source == source;
+}
+
+MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
+    *result_listener << "expected displayId " << displayId << ", but got " << arg.displayId;
+    return arg.displayId == displayId;
+}
+
+MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
+    const auto argX = arg.pointerCoords[0].getX();
+    const auto argY = arg.pointerCoords[0].getY();
+    *result_listener << "expected coords (" << x << ", " << y << "), but got (" << argX << ", "
+                     << argY << ")";
+    return argX == x && argY == y;
+}
+
+MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
+    const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+    *result_listener << "expected pressure " << pressure << ", but got " << pressure;
+    return argPressure;
+}
+
+MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
+    *result_listener << "expected flags " << flags << ", but got " << arg.flags;
+    return arg.flags == flags;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index a37fc2b..e0ff8c3 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _UI_TEST_INPUT_UINPUT_INJECTOR_H
-#define _UI_TEST_INPUT_UINPUT_INJECTOR_H
+#pragma once
 
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
@@ -155,5 +154,3 @@
 };
 
 } // namespace android
-
-#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index f2a3964..4c84160 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -16,12 +16,17 @@
 
 #include "../UnwantedInteractionBlocker.h"
 #include <android-base/silent_death_test.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <gui/constants.h>
 #include <linux/input.h>
 #include <thread>
+#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
 
 #include "TestInputListener.h"
+#include "TestInputListenerMatchers.h"
+
+using ::testing::AllOf;
 
 namespace android {
 
@@ -30,6 +35,8 @@
 constexpr int32_t Y_RESOLUTION = 11;
 constexpr int32_t MAJOR_RESOLUTION = 1;
 
+const nsecs_t RESAMPLE_PERIOD = ::ui::kResamplePeriod.InNanoseconds();
+
 constexpr int POINTER_0_DOWN =
         AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 constexpr int POINTER_1_DOWN =
@@ -47,6 +54,8 @@
 constexpr int UP = AMOTION_EVENT_ACTION_UP;
 constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL;
 
+constexpr int32_t FLAG_CANCELED = AMOTION_EVENT_FLAG_CANCELED;
+
 static nsecs_t toNs(std::chrono::nanoseconds duration) {
     return duration.count();
 }
@@ -256,7 +265,7 @@
                                      /*newSuppressedPointerIds*/ {1});
     ASSERT_EQ(2u, result.size());
     assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
     assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}});
 }
 
@@ -271,7 +280,7 @@
                                      /*newSuppressedPointerIds*/ {0});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -286,7 +295,7 @@
                                      /*newSuppressedPointerIds*/ {1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -301,7 +310,7 @@
                                      /*newSuppressedPointerIds*/ {0});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -316,7 +325,7 @@
                                      /*newSuppressedPointerIds*/ {0, 1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -332,7 +341,7 @@
                                      /*newSuppressedPointerIds*/ {0, 1});
     ASSERT_EQ(1u, result.size());
     assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}});
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, result[0].flags);
 }
 
 /**
@@ -573,6 +582,114 @@
     dumpThread.join();
 }
 
+/**
+ * Heuristic filter that's present in the palm rejection model blocks touches early if the size
+ * of the touch is large. This is an integration test that checks that this filter kicks in.
+ */
+TEST_F(UnwantedInteractionBlockerTest, HeuristicFilterWorks) {
+    mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()});
+    // Small touch down
+    NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+    mBlocker->notifyMotion(&args1);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
+
+    // Large touch oval on the next move
+    NotifyMotionArgs args2 =
+            generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
+    mBlocker->notifyMotion(&args2);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
+
+    // Lift up the touch to force the model to decide on whether it's a palm
+    NotifyMotionArgs args3 =
+            generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+    mBlocker->notifyMotion(&args3);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(CANCEL));
+}
+
+/**
+ * Send a stylus event that would have triggered the heuristic palm detector if it were a touch
+ * event. However, since it's a stylus event, it should propagate without being canceled through
+ * the blocker.
+ * This is similar to `HeuristicFilterWorks` test, but for stylus tool.
+ */
+TEST_F(UnwantedInteractionBlockerTest, StylusIsNotBlocked) {
+    InputDeviceInfo info = generateTestDeviceInfo();
+    info.addSource(AINPUT_SOURCE_STYLUS);
+    mBlocker->notifyInputDevicesChanged({info});
+    NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+    args1.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args1);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
+
+    // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions
+    NotifyMotionArgs args2 =
+            generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}});
+    args2.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args2);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
+
+    // Lift up the stylus. If it were a touch event, this would force the model to decide on whether
+    // it's a palm.
+    NotifyMotionArgs args3 =
+            generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+    args3.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args3);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP));
+}
+
+/**
+ * Send a mixed touch and stylus event.
+ * The touch event goes first, and is a palm. The stylus event goes down after.
+ * Stylus event should continue to work even after touch is detected as a palm.
+ */
+TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) {
+    InputDeviceInfo info = generateTestDeviceInfo();
+    info.addSource(AINPUT_SOURCE_STYLUS);
+    mBlocker->notifyInputDevicesChanged({info});
+
+    // Touch down
+    NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}});
+    mBlocker->notifyMotion(&args1);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(DOWN));
+
+    // Stylus pointer down
+    NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, POINTER_1_DOWN,
+                                                {{1, 2, 3}, {10, 20, 30}});
+    args2.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args2);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(POINTER_1_DOWN));
+
+    // Large touch oval on the next finger move
+    NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, MOVE,
+                                                {{1, 2, 300}, {11, 21, 30}});
+    args3.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args3);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
+
+    // Lift up the finger pointer. It should be canceled due to the heuristic filter.
+    NotifyMotionArgs args4 = generateMotionArgs(0 /*downTime*/, 3 * RESAMPLE_PERIOD, POINTER_0_UP,
+                                                {{1, 2, 300}, {11, 21, 30}});
+    args4.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args4);
+    mTestListener.assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(POINTER_0_UP), WithFlags(FLAG_CANCELED)));
+
+    NotifyMotionArgs args5 =
+            generateMotionArgs(0 /*downTime*/, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}});
+    args5.pointerProperties[0].id = args4.pointerProperties[1].id;
+    args5.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args5);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(MOVE));
+
+    // Lift up the stylus pointer
+    NotifyMotionArgs args6 =
+            generateMotionArgs(0 /*downTime*/, 5 * RESAMPLE_PERIOD, UP, {{4, 5, 200}});
+    args6.pointerProperties[0].id = args4.pointerProperties[1].id;
+    args6.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+    mBlocker->notifyMotion(&args6);
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(UP));
+}
+
 using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest;
 
 /**
@@ -672,7 +789,7 @@
                                {{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}}));
     ASSERT_EQ(2u, argsList.size());
     ASSERT_EQ(POINTER_0_UP, argsList[0].action);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
     ASSERT_EQ(MOVE, argsList[1].action);
     ASSERT_EQ(1u, argsList[1].pointerCount);
     ASSERT_EQ(0, argsList[1].flags);
@@ -849,7 +966,7 @@
     ASSERT_EQ(2u, argsList.size());
     // First event - cancel pointer 1
     ASSERT_EQ(POINTER_1_UP, argsList[0].action);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
     // Second event - send MOVE for the remaining pointer
     ASSERT_EQ(MOVE, argsList[1].action);
     ASSERT_EQ(0, argsList[1].flags);
@@ -890,7 +1007,7 @@
     // Cancel all
     ASSERT_EQ(CANCEL, argsList[0].action);
     ASSERT_EQ(2u, argsList[0].pointerCount);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
 
     // Future move events are ignored
     argsList = mPalmRejector->processMotion(
@@ -936,7 +1053,7 @@
                                {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}}));
     ASSERT_EQ(1u, argsList.size());
     ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
 
     // Future move events should not go to the listener.
     argsList = mPalmRejector->processMotion(
@@ -970,7 +1087,7 @@
                                {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}}));
     ASSERT_EQ(2u, argsList.size());
     ASSERT_EQ(POINTER_1_UP, argsList[0].action);
-    ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags);
+    ASSERT_EQ(FLAG_CANCELED, argsList[0].flags);
 
     ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action);
     ASSERT_EQ(0, argsList[1].flags);
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 455a1e2..55c2db6 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -21,7 +21,6 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-
 cc_fuzz {
     name: "inputflinger_latencytracker_fuzzer",
     defaults: [
@@ -34,7 +33,6 @@
         "libbase",
         "libbinder",
         "liblog",
-        "libui",
         "libutils",
         "libinput",
         "libinputflinger",
@@ -43,6 +41,107 @@
         "LatencyTrackerFuzzer.cpp",
     ],
     fuzz_config: {
-       cc: ["android-framework-input@google.com"],
+        cc: ["android-framework-input@google.com"],
     },
 }
+
+cc_defaults {
+    name: "inputflinger_fuzz_defaults",
+    defaults: [
+        "inputflinger_defaults",
+    ],
+    include_dirs: [
+        "frameworks/native/services/inputflinger",
+    ],
+    shared_libs: [
+        "android.hardware.input.classifier@1.0",
+        "android.hardware.input.processor-V1-ndk",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "libinput",
+        "libinputflinger",
+        "libinputreader",
+        "libinputflinger_base",
+        "libstatslog",
+    ],
+    header_libs: [
+        "libbatteryservice_headers",
+        "libinputreader_headers",
+    ],
+    fuzz_config: {
+        cc: ["android-framework-input@google.com"],
+    },
+}
+
+cc_fuzz {
+    name: "inputflinger_cursor_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "CursorInputFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_keyboard_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "KeyboardInputFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_multitouch_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "MultiTouchInputFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_switch_input_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "SwitchInputFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_input_reader_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "InputReaderFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_blocking_queue_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "BlockingQueueFuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "inputflinger_input_classifier_fuzzer",
+    defaults: [
+        "inputflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "InputClassifierFuzzer.cpp",
+    ],
+}
diff --git a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
new file mode 100644
index 0000000..d2595bf
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <thread>
+#include "BlockingQueue.h"
+
+// Chosen to be a number large enough for variation in fuzzer runs, but not consume too much memory.
+static constexpr size_t MAX_CAPACITY = 1024;
+
+namespace android {
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+    size_t capacity = fdp.ConsumeIntegralInRange<size_t>(1, MAX_CAPACITY);
+    size_t filled = 0;
+    BlockingQueue<int32_t> queue(capacity);
+
+    while (fdp.remaining_bytes() > 0) {
+        fdp.PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    size_t numPushes = fdp.ConsumeIntegralInRange<size_t>(0, capacity + 1);
+                    for (size_t i = 0; i < numPushes; i++) {
+                        queue.push(fdp.ConsumeIntegral<int32_t>());
+                    }
+                    filled = std::min(capacity, filled + numPushes);
+                },
+                [&]() -> void {
+                    // Pops blocks if it is empty, so only pop up to num elements inserted.
+                    size_t numPops = fdp.ConsumeIntegralInRange<size_t>(0, filled);
+                    for (size_t i = 0; i < numPops; i++) {
+                        queue.pop();
+                    }
+                    filled > numPops ? filled -= numPops : filled = 0;
+                },
+                [&]() -> void {
+                    queue.clear();
+                    filled = 0;
+                },
+                [&]() -> void {
+                    int32_t eraseElement = fdp.ConsumeIntegral<int32_t>();
+                    queue.erase([&](int32_t element) {
+                        if (element == eraseElement) {
+                            filled--;
+                            return true;
+                        }
+                        return false;
+                    });
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
new file mode 100644
index 0000000..cc523e1
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <CursorInputMapper.h>
+#include <FuzzContainer.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+    // Pick a random property to set for the mapper to have set.
+    fdp->PickValueInArray<std::function<void()>>(
+            {[&]() -> void { fuzzer.addProperty("cursor.mode", "pointer"); },
+             [&]() -> void { fuzzer.addProperty("cursor.mode", "navigation"); },
+             [&]() -> void {
+                 fuzzer.addProperty("cursor.mode", fdp->ConsumeRandomLengthString(100).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("cursor.orientationAware",
+                                    fdp->ConsumeRandomLengthString(100).data());
+             }})();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+
+    CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>();
+    auto policyConfig = fuzzer.getPolicyConfig();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void { addProperty(fuzzer, fdp); },
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                             fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    // Need to reconfigure with 0 or you risk a NPE.
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    InputDeviceInfo info;
+                    mapper.populateDeviceInfo(&info);
+                },
+                [&]() -> void {
+                    int32_t type, code;
+                    type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+                    code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+
+                    // Need to reconfigure with 0 or you risk a NPE.
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<int32_t>(),
+                                      type,
+                                      code,
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    unused += mapper.process(&rawEvent);
+                },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+                },
+                [&]() -> void {
+                    mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    // Need to reconfigure with 0 or you risk a NPE.
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    mapper.getAssociatedDisplayId();
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
new file mode 100644
index 0000000..1e0764f
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <InputReader.h>
+#include <MapperHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+class FuzzContainer {
+    std::shared_ptr<FuzzEventHub> mFuzzEventHub;
+    sp<FuzzInputReaderPolicy> mFuzzPolicy;
+    FuzzInputListener mFuzzListener;
+    std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
+    std::unique_ptr<InputDevice> mFuzzDevice;
+    InputReaderConfiguration mPolicyConfig;
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+public:
+    FuzzContainer(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(fdp) {
+        // Setup parameters.
+        std::string deviceName = mFdp->ConsumeRandomLengthString(16);
+        std::string deviceLocation = mFdp->ConsumeRandomLengthString(12);
+        int32_t deviceID = mFdp->ConsumeIntegralInRange<int32_t>(0, 5);
+        int32_t deviceGeneration = mFdp->ConsumeIntegralInRange<int32_t>(/*from*/ 0, /*to*/ 5);
+
+        // Create mocked objects.
+        mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp);
+        mFuzzPolicy = sp<FuzzInputReaderPolicy>::make(mFdp);
+        mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy,
+                                                                mFuzzListener, mFdp);
+
+        InputDeviceIdentifier identifier;
+        identifier.name = deviceName;
+        identifier.location = deviceLocation;
+        mFuzzDevice = std::make_unique<InputDevice>(mFuzzContext.get(), deviceID, deviceGeneration,
+                                                    identifier);
+        mFuzzPolicy->getReaderConfiguration(&mPolicyConfig);
+    }
+
+    ~FuzzContainer() {}
+
+    void configureDevice() {
+        nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
+        std::list<NotifyArgs> out;
+        out += mFuzzDevice->configure(arbitraryTime, &mPolicyConfig, 0);
+        out += mFuzzDevice->reset(arbitraryTime);
+        for (const NotifyArgs& args : out) {
+            mFuzzListener.notify(args);
+        }
+    }
+
+    void addProperty(std::string key, std::string value) {
+        mFuzzEventHub->addProperty(key, value);
+        configureDevice();
+    }
+
+    InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; }
+
+    template <class T, typename... Args>
+    T& getMapper(Args... args) {
+        T& mapper = mFuzzDevice->addMapper<T>(mFdp->ConsumeIntegral<int32_t>(), args...);
+        configureDevice();
+        return mapper;
+    }
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
new file mode 100644
index 0000000..c407cff
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <MapperHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "InputCommonConverter.h"
+#include "InputProcessor.h"
+
+namespace android {
+
+static constexpr int32_t MAX_AXES = 64;
+
+// Used by two fuzz operations and a bit lengthy, so pulled out into a function.
+NotifyMotionArgs generateFuzzedMotionArgs(FuzzedDataProvider &fdp) {
+    // Create a basic motion event for testing
+    PointerProperties properties;
+    properties.id = 0;
+    properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+    PointerCoords coords;
+    coords.clear();
+    for (int32_t i = 0; i < fdp.ConsumeIntegralInRange<int32_t>(0, MAX_AXES); i++) {
+        coords.setAxisValue(fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeFloatingPoint<float>());
+    }
+
+    const nsecs_t downTime = 2;
+    const nsecs_t readTime = downTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8);
+    NotifyMotionArgs motionArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                downTime /*eventTime*/, readTime,
+                                fdp.ConsumeIntegral<int32_t>() /*deviceId*/, AINPUT_SOURCE_ANY,
+                                ADISPLAY_ID_DEFAULT,
+                                fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
+                                AMOTION_EVENT_ACTION_DOWN,
+                                fdp.ConsumeIntegral<int32_t>() /*actionButton*/,
+                                fdp.ConsumeIntegral<int32_t>() /*flags*/, AMETA_NONE,
+                                fdp.ConsumeIntegral<int32_t>() /*buttonState*/,
+                                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                1 /*pointerCount*/, &properties, &coords,
+                                fdp.ConsumeFloatingPoint<float>() /*xPrecision*/,
+                                fdp.ConsumeFloatingPoint<float>() /*yPrecision*/,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
+                                {} /*videoFrames*/);
+    return motionArgs;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+
+    std::unique_ptr<FuzzInputListener> mFuzzListener = std::make_unique<FuzzInputListener>();
+    std::unique_ptr<InputProcessorInterface> mClassifier =
+            std::make_unique<InputProcessor>(*mFuzzListener);
+
+    while (fdp.remaining_bytes() > 0) {
+        fdp.PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    // SendToNextStage_NotifyConfigurationChangedArgs
+                    NotifyConfigurationChangedArgs
+                            args(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                 fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/);
+                    mClassifier->notifyConfigurationChanged(&args);
+                },
+                [&]() -> void {
+                    // SendToNextStage_NotifyKeyArgs
+                    const nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
+                    const nsecs_t readTime =
+                            eventTime + fdp.ConsumeIntegralInRange<nsecs_t>(0, 1E8);
+                    NotifyKeyArgs keyArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                          eventTime, readTime,
+                                          fdp.ConsumeIntegral<int32_t>() /*deviceId*/,
+                                          AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT,
+                                          fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
+                                          AKEY_EVENT_ACTION_DOWN,
+                                          fdp.ConsumeIntegral<int32_t>() /*flags*/, AKEYCODE_HOME,
+                                          fdp.ConsumeIntegral<int32_t>() /*scanCode*/, AMETA_NONE,
+                                          fdp.ConsumeIntegral<nsecs_t>() /*downTime*/);
+
+                    mClassifier->notifyKey(&keyArgs);
+                },
+                [&]() -> void {
+                    // SendToNextStage_NotifyMotionArgs
+                    NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
+                    mClassifier->notifyMotion(&motionArgs);
+                },
+                [&]() -> void {
+                    // SendToNextStage_NotifySwitchArgs
+                    NotifySwitchArgs switchArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                                fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/,
+                                                fdp.ConsumeIntegral<uint32_t>() /*policyFlags*/,
+                                                fdp.ConsumeIntegral<uint32_t>() /*switchValues*/,
+                                                fdp.ConsumeIntegral<uint32_t>() /*switchMask*/);
+
+                    mClassifier->notifySwitch(&switchArgs);
+                },
+                [&]() -> void {
+                    // SendToNextStage_NotifyDeviceResetArgs
+                    NotifyDeviceResetArgs resetArgs(fdp.ConsumeIntegral<uint32_t>() /*sequenceNum*/,
+                                                    fdp.ConsumeIntegral<nsecs_t>() /*eventTime*/,
+                                                    fdp.ConsumeIntegral<int32_t>() /*deviceId*/);
+
+                    mClassifier->notifyDeviceReset(&resetArgs);
+                },
+                [&]() -> void {
+                    // InputClassifierConverterTest
+                    const NotifyMotionArgs motionArgs = generateFuzzedMotionArgs(fdp);
+                    aidl::android::hardware::input::common::MotionEvent motionEvent =
+                            notifyMotionArgsToHalMotionEvent(motionArgs);
+                },
+        })();
+    }
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
new file mode 100644
index 0000000..a9f5a3a
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <InputReader.h>
+#include <MapperHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <input/InputDevice.h>
+#include <chrono>
+#include <thread>
+
+namespace android {
+
+constexpr InputDeviceSensorType kInputDeviceSensorType[] = {
+        InputDeviceSensorType::ACCELEROMETER,
+        InputDeviceSensorType::MAGNETIC_FIELD,
+        InputDeviceSensorType::ORIENTATION,
+        InputDeviceSensorType::GYROSCOPE,
+        InputDeviceSensorType::LIGHT,
+        InputDeviceSensorType::PRESSURE,
+        InputDeviceSensorType::TEMPERATURE,
+        InputDeviceSensorType::PROXIMITY,
+        InputDeviceSensorType::GRAVITY,
+        InputDeviceSensorType::LINEAR_ACCELERATION,
+        InputDeviceSensorType::ROTATION_VECTOR,
+        InputDeviceSensorType::RELATIVE_HUMIDITY,
+        InputDeviceSensorType::AMBIENT_TEMPERATURE,
+        InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED,
+        InputDeviceSensorType::GAME_ROTATION_VECTOR,
+        InputDeviceSensorType::GYROSCOPE_UNCALIBRATED,
+        InputDeviceSensorType::SIGNIFICANT_MOTION,
+};
+
+class FuzzInputReader : public InputReaderInterface {
+public:
+    FuzzInputReader(std::shared_ptr<EventHubInterface> fuzzEventHub,
+                    const sp<InputReaderPolicyInterface>& fuzzPolicy,
+                    InputListenerInterface& fuzzListener) {
+        reader = std::make_unique<InputReader>(fuzzEventHub, fuzzPolicy, fuzzListener);
+    }
+
+    void dump(std::string& dump) { reader->dump(dump); }
+
+    void monitor() { reader->monitor(); }
+
+    bool isInputDeviceEnabled(int32_t deviceId) { return reader->isInputDeviceEnabled(deviceId); }
+
+    status_t start() { return reader->start(); }
+
+    status_t stop() { return reader->stop(); }
+
+    std::vector<InputDeviceInfo> getInputDevices() const { return reader->getInputDevices(); }
+
+    int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) {
+        return reader->getScanCodeState(deviceId, sourceMask, scanCode);
+    }
+
+    int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) {
+        return reader->getKeyCodeState(deviceId, sourceMask, keyCode);
+    }
+
+    int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) {
+        return reader->getSwitchState(deviceId, sourceMask, sw);
+    }
+
+    void toggleCapsLockState(int32_t deviceId) { reader->toggleCapsLockState(deviceId); }
+
+    bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
+                 uint8_t* outFlags) {
+        return reader->hasKeys(deviceId, sourceMask, keyCodes, outFlags);
+    }
+
+    void requestRefreshConfiguration(uint32_t changes) {
+        reader->requestRefreshConfiguration(changes);
+    }
+
+    void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+                 int32_t token) {
+        reader->vibrate(deviceId, sequence, repeat, token);
+    }
+
+    void cancelVibrate(int32_t deviceId, int32_t token) { reader->cancelVibrate(deviceId, token); }
+
+    bool isVibrating(int32_t deviceId) { return reader->isVibrating(deviceId); }
+
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) {
+        return reader->getVibratorIds(deviceId);
+    }
+
+    std::optional<int32_t> getBatteryCapacity(int32_t deviceId) {
+        return reader->getBatteryCapacity(deviceId);
+    }
+
+    std::optional<int32_t> getBatteryStatus(int32_t deviceId) {
+        return reader->getBatteryStatus(deviceId);
+    }
+
+    std::optional<std::string> getBatteryDevicePath(int32_t deviceId) {
+        return reader->getBatteryDevicePath(deviceId);
+    }
+
+    std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) {
+        return reader->getLights(deviceId);
+    }
+
+    std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) {
+        return reader->getSensors(deviceId);
+    }
+
+    bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+        return reader->canDispatchToDisplay(deviceId, displayId);
+    }
+
+    bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+                      std::chrono::microseconds samplingPeriod,
+                      std::chrono::microseconds maxBatchReportLatency) {
+        return reader->enableSensor(deviceId, sensorType, samplingPeriod, maxBatchReportLatency);
+    }
+
+    void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) {
+        return reader->disableSensor(deviceId, sensorType);
+    }
+
+    void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) {
+        return reader->flushSensor(deviceId, sensorType);
+    }
+
+    bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) {
+        return reader->setLightColor(deviceId, lightId, color);
+    }
+
+    bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) {
+        return reader->setLightPlayerId(deviceId, lightId, playerId);
+    }
+
+    std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) {
+        return reader->getLightColor(deviceId, lightId);
+    }
+
+    std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) {
+        return reader->getLightPlayerId(deviceId, lightId);
+    }
+
+    int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
+        return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
+    }
+
+private:
+    std::unique_ptr<InputReaderInterface> reader;
+};
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+
+    FuzzInputListener fuzzListener;
+    sp<FuzzInputReaderPolicy> fuzzPolicy = sp<FuzzInputReaderPolicy>::make(fdp);
+    std::shared_ptr<FuzzEventHub> fuzzEventHub = std::make_shared<FuzzEventHub>(fdp);
+    std::unique_ptr<FuzzInputReader> reader =
+            std::make_unique<FuzzInputReader>(fuzzEventHub, fuzzPolicy, fuzzListener);
+    size_t patternCount = fdp->ConsumeIntegralInRange<size_t>(1, 260);
+    VibrationSequence pattern(patternCount);
+    for (size_t i = 0; i < patternCount; ++i) {
+        VibrationElement element(i);
+        element.addChannel(fdp->ConsumeIntegral<int32_t>() /* vibratorId */,
+                           fdp->ConsumeIntegral<uint8_t>() /* amplitude */);
+        pattern.addElement(element);
+    }
+    reader->vibrate(fdp->ConsumeIntegral<int32_t>(), pattern,
+                    fdp->ConsumeIntegral<ssize_t>() /*repeat*/,
+                    fdp->ConsumeIntegral<int32_t>() /*token*/);
+    reader->start();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    std::string dump;
+                    reader->dump(dump);
+                },
+                [&]() -> void { reader->monitor(); },
+                [&]() -> void { reader->getInputDevices(); },
+                [&]() -> void { reader->isInputDeviceEnabled(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void {
+                    reader->getScanCodeState(fdp->ConsumeIntegral<int32_t>(),
+                                             fdp->ConsumeIntegral<uint32_t>(),
+                                             fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->getKeyCodeState(fdp->ConsumeIntegral<int32_t>(),
+                                            fdp->ConsumeIntegral<uint32_t>(),
+                                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->getSwitchState(fdp->ConsumeIntegral<int32_t>(),
+                                           fdp->ConsumeIntegral<uint32_t>(),
+                                           fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void { reader->toggleCapsLockState(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void {
+                    size_t count = fdp->ConsumeIntegralInRange<size_t>(1, 1024);
+                    std::vector<uint8_t> outFlags(count);
+                    std::vector<int32_t> keyCodes;
+                    for (size_t i = 0; i < count; ++i) {
+                        keyCodes.push_back(fdp->ConsumeIntegral<int32_t>());
+                    }
+                    reader->hasKeys(fdp->ConsumeIntegral<int32_t>(),
+                                    fdp->ConsumeIntegral<uint32_t>(), keyCodes, outFlags.data());
+                },
+                [&]() -> void {
+                    reader->requestRefreshConfiguration(fdp->ConsumeIntegral<uint32_t>());
+                },
+                [&]() -> void {
+                    reader->cancelVibrate(fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->canDispatchToDisplay(fdp->ConsumeIntegral<int32_t>(),
+                                                 fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->getKeyCodeForKeyLocation(fdp->ConsumeIntegral<int32_t>(),
+                                                     fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void { reader->getBatteryCapacity(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { reader->getBatteryStatus(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { reader->getBatteryDevicePath(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { reader->getLights(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { reader->getSensors(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void {
+                    reader->getLightPlayerId(fdp->ConsumeIntegral<int32_t>(),
+                                             fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->getLightColor(fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->setLightPlayerId(fdp->ConsumeIntegral<int32_t>(),
+                                             fdp->ConsumeIntegral<int32_t>(),
+                                             fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->setLightColor(fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    reader->flushSensor(fdp->ConsumeIntegral<int32_t>(),
+                                        fdp->PickValueInArray<InputDeviceSensorType>(
+                                                kInputDeviceSensorType));
+                },
+                [&]() -> void {
+                    reader->disableSensor(fdp->ConsumeIntegral<int32_t>(),
+                                          fdp->PickValueInArray<InputDeviceSensorType>(
+                                                  kInputDeviceSensorType));
+                },
+                [&]() -> void {
+                    reader->enableSensor(fdp->ConsumeIntegral<int32_t>(),
+                                         fdp->PickValueInArray<InputDeviceSensorType>(
+                                                 kInputDeviceSensorType),
+                                         std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()),
+                                         std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()));
+                },
+        })();
+    }
+
+    reader->stop();
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
new file mode 100644
index 0000000..e880f55
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <FuzzContainer.h>
+#include <KeyboardInputMapper.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+const int32_t kMaxKeycodes = 100;
+
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+    // Pick a random property to set for the mapper to have set.
+    fdp->PickValueInArray<std::function<void()>>(
+            {[&]() -> void { fuzzer.addProperty("keyboard.orientationAware", "1"); },
+             [&]() -> void {
+                 fuzzer.addProperty("keyboard.orientationAware",
+                                    fdp->ConsumeRandomLengthString(100).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("keyboard.doNotWakeByDefault",
+                                    fdp->ConsumeRandomLengthString(100).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("keyboard.handlesKeyRepeat",
+                                    fdp->ConsumeRandomLengthString(100).data());
+             }})();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+
+    KeyboardInputMapper& mapper =
+            fuzzer.getMapper<KeyboardInputMapper>(fdp->ConsumeIntegral<uint32_t>(),
+                                                  fdp->ConsumeIntegral<int32_t>());
+    auto policyConfig = fuzzer.getPolicyConfig();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void { addProperty(fuzzer, fdp); },
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void {
+                    InputDeviceInfo info;
+                    mapper.populateDeviceInfo(&info);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                             fdp->ConsumeIntegral<uint32_t>());
+                },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+                },
+                [&]() -> void {
+                    int32_t type, code;
+                    type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+                    code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                              : fdp->ConsumeIntegral<int32_t>();
+                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<int32_t>(),
+                                      type,
+                                      code,
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+                },
+                [&]() -> void {
+                    mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                           fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    std::vector<int32_t> keyCodes;
+                    int32_t numBytes = fdp->ConsumeIntegralInRange<int32_t>(0, kMaxKeycodes);
+                    for (int32_t i = 0; i < numBytes; ++i) {
+                        keyCodes.push_back(fdp->ConsumeIntegral<int32_t>());
+                    }
+                    mapper.markSupportedKeyCodes(fdp->ConsumeIntegral<uint32_t>(), keyCodes,
+                                                 nullptr);
+                },
+                [&]() -> void { mapper.getMetaState(); },
+                [&]() -> void { mapper.updateMetaState(fdp->ConsumeIntegral<int32_t>()); },
+                [&]() -> void { mapper.getAssociatedDisplayId(); },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
new file mode 100644
index 0000000..bd81761
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <InputReader.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "android/hardware/input/InputDeviceCountryCode.h"
+
+using android::hardware::input::InputDeviceCountryCode;
+
+constexpr size_t kValidTypes[] = {EV_SW,
+                                  EV_SYN,
+                                  SYN_REPORT,
+                                  EV_ABS,
+                                  EV_KEY,
+                                  EV_MSC,
+                                  EV_REL,
+                                  android::EventHubInterface::DEVICE_ADDED,
+                                  android::EventHubInterface::DEVICE_REMOVED,
+                                  android::EventHubInterface::FINISHED_DEVICE_SCAN};
+
+constexpr size_t kValidCodes[] = {
+        SYN_REPORT,
+        ABS_MT_SLOT,
+        SYN_MT_REPORT,
+        ABS_MT_POSITION_X,
+        ABS_MT_POSITION_Y,
+        ABS_MT_TOUCH_MAJOR,
+        ABS_MT_TOUCH_MINOR,
+        ABS_MT_WIDTH_MAJOR,
+        ABS_MT_WIDTH_MINOR,
+        ABS_MT_ORIENTATION,
+        ABS_MT_TRACKING_ID,
+        ABS_MT_PRESSURE,
+        ABS_MT_DISTANCE,
+        ABS_MT_TOOL_TYPE,
+        SYN_MT_REPORT,
+        MSC_SCAN,
+        REL_X,
+        REL_Y,
+        REL_WHEEL,
+        REL_HWHEEL,
+        BTN_LEFT,
+        BTN_RIGHT,
+        BTN_MIDDLE,
+        BTN_BACK,
+        BTN_SIDE,
+        BTN_FORWARD,
+        BTN_EXTRA,
+        BTN_TASK,
+};
+
+constexpr InputDeviceCountryCode kCountryCodes[] = {
+        InputDeviceCountryCode::INVALID,
+        InputDeviceCountryCode::NOT_SUPPORTED,
+        InputDeviceCountryCode::ARABIC,
+        InputDeviceCountryCode::BELGIAN,
+        InputDeviceCountryCode::CANADIAN_BILINGUAL,
+        InputDeviceCountryCode::CANADIAN_FRENCH,
+        InputDeviceCountryCode::CZECH_REPUBLIC,
+        InputDeviceCountryCode::DANISH,
+        InputDeviceCountryCode::FINNISH,
+        InputDeviceCountryCode::FRENCH,
+        InputDeviceCountryCode::GERMAN,
+        InputDeviceCountryCode::GREEK,
+        InputDeviceCountryCode::HEBREW,
+        InputDeviceCountryCode::HUNGARY,
+        InputDeviceCountryCode::INTERNATIONAL,
+        InputDeviceCountryCode::ITALIAN,
+        InputDeviceCountryCode::JAPAN,
+        InputDeviceCountryCode::KOREAN,
+        InputDeviceCountryCode::LATIN_AMERICAN,
+        InputDeviceCountryCode::DUTCH,
+        InputDeviceCountryCode::NORWEGIAN,
+        InputDeviceCountryCode::PERSIAN,
+        InputDeviceCountryCode::POLAND,
+        InputDeviceCountryCode::PORTUGUESE,
+        InputDeviceCountryCode::RUSSIA,
+        InputDeviceCountryCode::SLOVAKIA,
+        InputDeviceCountryCode::SPANISH,
+        InputDeviceCountryCode::SWEDISH,
+        InputDeviceCountryCode::SWISS_FRENCH,
+        InputDeviceCountryCode::SWISS_GERMAN,
+        InputDeviceCountryCode::SWITZERLAND,
+        InputDeviceCountryCode::TAIWAN,
+        InputDeviceCountryCode::TURKISH_Q,
+        InputDeviceCountryCode::UK,
+        InputDeviceCountryCode::US,
+        InputDeviceCountryCode::YUGOSLAVIA,
+        InputDeviceCountryCode::TURKISH_F,
+};
+
+constexpr size_t kMaxSize = 256;
+
+namespace android {
+
+class FuzzEventHub : public EventHubInterface {
+    InputDeviceIdentifier mIdentifier;
+    std::vector<TouchVideoFrame> mVideoFrames;
+    PropertyMap mFuzzConfig;
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+public:
+    FuzzEventHub(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(std::move(fdp)) {}
+    ~FuzzEventHub() {}
+    void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); }
+
+    ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
+        return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>());
+    }
+    InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
+        return mIdentifier;
+    }
+    int32_t getDeviceControllerNumber(int32_t deviceId) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
+        *outConfiguration = mFuzzConfig;
+    }
+    status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+                                 RawAbsoluteAxisInfo* outAxisInfo) const override {
+        return mFdp->ConsumeIntegral<status_t>();
+    }
+    bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); }
+    bool hasInputProperty(int32_t deviceId, int property) const override {
+        return mFdp->ConsumeBool();
+    }
+    bool hasMscEvent(int32_t deviceId, int mscEvent) const override { return mFdp->ConsumeBool(); }
+    status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+                    int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
+        return mFdp->ConsumeIntegral<status_t>();
+    }
+    status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const override {
+        return mFdp->ConsumeIntegral<status_t>();
+    }
+    void setExcludedDevices(const std::vector<std::string>& devices) override {}
+    std::vector<RawEvent> getEvents(int timeoutMillis) override {
+        std::vector<RawEvent> events;
+        const size_t count = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
+        for (size_t i = 0; i < count; ++i) {
+            int32_t type = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidTypes)
+                                               : mFdp->ConsumeIntegral<int32_t>();
+            int32_t code = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidCodes)
+                                               : mFdp->ConsumeIntegral<int32_t>();
+            events.push_back({
+                    .when = mFdp->ConsumeIntegral<nsecs_t>(),
+                    .readTime = mFdp->ConsumeIntegral<nsecs_t>(),
+                    .deviceId = mFdp->ConsumeIntegral<int32_t>(),
+                    .type = type,
+                    .code = code,
+                    .value = mFdp->ConsumeIntegral<int32_t>(),
+            });
+        }
+        return events;
+    }
+    std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { return mVideoFrames; }
+
+    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+            int32_t deviceId, int32_t absCode) const override {
+        return base::ResultError("Fuzzer", UNKNOWN_ERROR);
+    };
+    // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node,
+    // containing the raw info of the sysfs node structure.
+    std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override { return {}; }
+    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+                                                    int32_t BatteryId) const override {
+        return std::nullopt;
+    };
+
+    std::vector<int32_t> getRawLightIds(int32_t deviceId) const override { return {}; };
+    std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override {
+        return std::nullopt;
+    };
+    std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override {
+        return std::nullopt;
+    };
+    void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override{};
+    std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+            int32_t deviceId, int32_t lightId) const override {
+        return std::nullopt;
+    };
+    void setLightIntensities(int32_t deviceId, int32_t lightId,
+                             std::unordered_map<LightColor, int32_t> intensities) override{};
+
+    InputDeviceCountryCode getCountryCode(int32_t deviceId) const override {
+        return mFdp->PickValueInArray<InputDeviceCountryCode>(kCountryCodes);
+    };
+
+    int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
+        return mFdp->ConsumeIntegral<int32_t>();
+    }
+    status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                  int32_t* outValue) const override {
+        return mFdp->ConsumeIntegral<status_t>();
+    }
+    bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
+                               uint8_t* outFlags) const override {
+        return mFdp->ConsumeBool();
+    }
+    bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
+        return mFdp->ConsumeBool();
+    }
+    bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override {
+        return mFdp->ConsumeBool();
+    }
+    bool hasLed(int32_t deviceId, int32_t led) const override { return mFdp->ConsumeBool(); }
+    void setLedState(int32_t deviceId, int32_t led, bool on) override {}
+    void getVirtualKeyDefinitions(
+            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {}
+    const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override {
+        return nullptr;
+    }
+    bool setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) override {
+        return mFdp->ConsumeBool();
+    }
+    void vibrate(int32_t deviceId, const VibrationElement& effect) override {}
+    void cancelVibrate(int32_t deviceId) override {}
+
+    std::vector<int32_t> getVibratorIds(int32_t deviceId) const override { return {}; };
+
+    /* Query battery level. */
+    std::optional<int32_t> getBatteryCapacity(int32_t deviceId, int32_t batteryId) const override {
+        return std::nullopt;
+    };
+
+    /* Query battery status. */
+    std::optional<int32_t> getBatteryStatus(int32_t deviceId, int32_t batteryId) const override {
+        return std::nullopt;
+    };
+
+    void requestReopenDevices() override {}
+    void wake() override {}
+    void dump(std::string& dump) const override {}
+    void monitor() const override {}
+    bool isDeviceEnabled(int32_t deviceId) const override { return mFdp->ConsumeBool(); }
+    status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
+    status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
+};
+
+class FuzzPointerController : public PointerControllerInterface {
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+public:
+    FuzzPointerController(std::shared_ptr<FuzzedDataProvider> mFdp) : mFdp(mFdp) {}
+    ~FuzzPointerController() {}
+    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
+        return mFdp->ConsumeBool();
+    }
+    void move(float deltaX, float deltaY) override {}
+    void setButtonState(int32_t buttonState) override {}
+    int32_t getButtonState() const override { return mFdp->ConsumeIntegral<int32_t>(); }
+    void setPosition(float x, float y) override {}
+    void getPosition(float* outX, float* outY) const override {}
+    void fade(Transition transition) override {}
+    void unfade(Transition transition) override {}
+    void setPresentation(Presentation presentation) override {}
+    void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                  BitSet32 spotIdBits, int32_t displayId) override {}
+    void clearSpots() override {}
+    int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); }
+    void setDisplayViewport(const DisplayViewport& displayViewport) override {}
+};
+
+class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
+    TouchAffineTransformation mTransform;
+    std::shared_ptr<FuzzPointerController> mPointerController;
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+protected:
+    ~FuzzInputReaderPolicy() {}
+
+public:
+    FuzzInputReaderPolicy(std::shared_ptr<FuzzedDataProvider> mFdp) : mFdp(mFdp) {
+        mPointerController = std::make_shared<FuzzPointerController>(mFdp);
+    }
+    void getReaderConfiguration(InputReaderConfiguration* outConfig) override {}
+    std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
+        return mPointerController;
+    }
+    void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
+    std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier& identifier) override {
+        return nullptr;
+    }
+    std::string getDeviceAlias(const InputDeviceIdentifier& identifier) {
+        return mFdp->ConsumeRandomLengthString(32);
+    }
+    TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
+                                                           int32_t surfaceRotation) override {
+        return mTransform;
+    }
+    void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
+};
+
+class FuzzInputListener : public virtual InputListenerInterface {
+public:
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override {}
+    void notifyKey(const NotifyKeyArgs* args) override {}
+    void notifyMotion(const NotifyMotionArgs* args) override {}
+    void notifySwitch(const NotifySwitchArgs* args) override {}
+    void notifySensor(const NotifySensorArgs* args) override{};
+    void notifyVibratorState(const NotifyVibratorStateArgs* args) override{};
+    void notifyDeviceReset(const NotifyDeviceResetArgs* args) override {}
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override{};
+};
+
+class FuzzInputReaderContext : public InputReaderContext {
+    std::shared_ptr<EventHubInterface> mEventHub;
+    sp<InputReaderPolicyInterface> mPolicy;
+    std::shared_ptr<FuzzedDataProvider> mFdp;
+
+public:
+    FuzzInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
+                           const sp<InputReaderPolicyInterface>& policy,
+                           InputListenerInterface& listener,
+                           std::shared_ptr<FuzzedDataProvider> mFdp)
+          : mEventHub(eventHub), mPolicy(policy), mFdp(mFdp) {}
+    ~FuzzInputReaderContext() {}
+    void updateGlobalMetaState() override {}
+    int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
+    void disableVirtualKeysUntil(nsecs_t time) override {}
+    bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override {
+        return mFdp->ConsumeBool();
+    }
+    void fadePointer() override {}
+    std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override {
+        return mPolicy->obtainPointerController(0);
+    }
+    void requestTimeoutAtTime(nsecs_t when) override {}
+    int32_t bumpGeneration() override { return mFdp->ConsumeIntegral<int32_t>(); }
+    void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {}
+    std::list<NotifyArgs> dispatchExternalStylusState(const StylusState& outState) override {
+        return {};
+    }
+    InputReaderPolicyInterface* getPolicy() override { return mPolicy.get(); }
+    EventHubInterface* getEventHub() override { return mEventHub.get(); }
+    int32_t getNextId() override { return mFdp->ConsumeIntegral<int32_t>(); }
+
+    void updateLedMetaState(int32_t metaState) override{};
+    int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
new file mode 100644
index 0000000..99fd083
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <FuzzContainer.h>
+#include <MultiTouchInputMapper.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+const int32_t kMaxKeycodes = 100;
+
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+    // Pick a random property to set for the mapper to have set.
+    fdp->PickValueInArray<std::function<void()>>(
+            {[&]() -> void { fuzzer.addProperty("touch.deviceType", "touchScreen"); },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.deviceType", fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.scale", fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.bias", fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.isSummed",
+                                    fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.calibration",
+                                    fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.pressure.scale",
+                                    fdp->ConsumeRandomLengthString(8).data());
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.size.calibration",
+                                    fdp->ConsumeBool() ? "diameter" : "area");
+             },
+             [&]() -> void {
+                 fuzzer.addProperty("touch.pressure.calibration",
+                                    fdp->ConsumeRandomLengthString(8).data());
+             }})();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+
+    MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>();
+    auto policyConfig = fuzzer.getPolicyConfig();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void { addProperty(fuzzer, fdp); },
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void {
+                    InputDeviceInfo info;
+                    mapper.populateDeviceInfo(&info);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                             fdp->ConsumeIntegral<uint32_t>());
+                },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+                },
+                [&]() -> void {
+                    int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                                      : fdp->ConsumeIntegral<int32_t>();
+                    int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                                      : fdp->ConsumeIntegral<int32_t>();
+                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<int32_t>(),
+                                      type,
+                                      code,
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+                },
+                [&]() -> void {
+                    mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                           fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
+                                            fdp->ConsumeIntegral<int32_t>());
+                },
+                [&]() -> void {
+                    std::vector<int32_t> keyCodes;
+                    int32_t numBytes = fdp->ConsumeIntegralInRange<int32_t>(0, kMaxKeycodes);
+                    for (int32_t i = 0; i < numBytes; ++i) {
+                        keyCodes.push_back(fdp->ConsumeIntegral<int32_t>());
+                    }
+                    mapper.markSupportedKeyCodes(fdp->ConsumeIntegral<uint32_t>(), keyCodes,
+                                                 nullptr);
+                },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused =
+                            mapper.cancelTouch(fdp->ConsumeIntegral<nsecs_t>(),
+                                               fdp->ConsumeIntegral<nsecs_t>());
+                },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused =
+                            mapper.timeoutExpired(fdp->ConsumeIntegral<nsecs_t>());
+                },
+                [&]() -> void {
+                    StylusState state{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeFloatingPoint<float>(),
+                                      fdp->ConsumeIntegral<uint32_t>(),
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    std::list<NotifyArgs> unused = mapper.updateExternalStylusState(state);
+                },
+                [&]() -> void { mapper.getAssociatedDisplayId(); },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
new file mode 100644
index 0000000..7416ce9
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <FuzzContainer.h>
+#include <SwitchInputMapper.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+    FuzzContainer fuzzer(fdp);
+
+    SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>();
+    auto policyConfig = fuzzer.getPolicyConfig();
+
+    // Loop through mapper operations until randomness is exhausted.
+    while (fdp->remaining_bytes() > 0) {
+        fdp->PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    std::string dump;
+                    mapper.dump(dump);
+                },
+                [&]() -> void { mapper.getSources(); },
+                [&]() -> void {
+                    int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
+                                                      : fdp->ConsumeIntegral<int32_t>();
+                    int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
+                                                      : fdp->ConsumeIntegral<int32_t>();
+                    RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<nsecs_t>(),
+                                      fdp->ConsumeIntegral<int32_t>(),
+                                      type,
+                                      code,
+                                      fdp->ConsumeIntegral<int32_t>()};
+                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+                },
+                [&]() -> void {
+                    mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(),
+                                          fdp->ConsumeIntegral<int32_t>());
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace android
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index 4d1de96..f67c610 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -726,7 +726,7 @@
             .type = type,
             .format = format,
             .size = static_cast<int32_t>(memory->size),
-            .memoryHandle = makeToAidl(memory->handle),
+            .memoryHandle = dupToAidl(memory->handle),
     };
 
     return convertToStatus(mSensors->registerDirectChannel(mem, channelHandle));
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 3c4f8d9..5ad4815 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -7,7 +7,7 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libsensorservice",
 
     srcs: [
@@ -90,6 +90,12 @@
     afdo: true,
 }
 
+cc_library_headers {
+    name: "libsensorservice_headers",
+    export_include_dirs: ["."],
+    visibility: ["//frameworks/native/services/sensorservice/fuzzer"],
+}
+
 cc_binary {
     name: "sensorservice",
 
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index de050e0..10ca990 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -39,7 +39,6 @@
 #include <thread>
 
 using namespace android::hardware::sensors;
-using android::hardware::Return;
 using android::util::ProtoOutputStream;
 
 namespace android {
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 2dd12e9..291c770 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "SensorDevice.h"
 #include "SensorDirectConnection.h"
 #include <android/util/ProtoOutputStream.h>
 #include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <hardware/sensors.h>
+#include "SensorDevice.h"
 
 #define UNUSED(x) (void)(x)
 
@@ -51,7 +51,7 @@
     stopAll();
     mService->cleanupConnection(this);
     if (mMem.handle != nullptr) {
-        native_handle_close(mMem.handle);
+        native_handle_close_with_tag(mMem.handle);
         native_handle_delete(const_cast<struct native_handle*>(mMem.handle));
     }
     mDestroyed = true;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 948692b..21d6b6b 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -16,7 +16,6 @@
 #include <android-base/strings.h>
 #include <android/content/pm/IPackageManagerNative.h>
 #include <android/util/ProtoOutputStream.h>
-#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <binder/ActivityManager.h>
 #include <binder/BinderService.h>
 #include <binder/IServiceManager.h>
@@ -25,6 +24,7 @@
 #include <cutils/ashmem.h>
 #include <cutils/misc.h>
 #include <cutils/properties.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <hardware/sensors.h>
 #include <hardware_legacy/power.h>
 #include <log/log.h>
@@ -1328,6 +1328,7 @@
             mSensors.getUserDebugSensors() : mSensors.getUserSensors();
     Vector<Sensor> accessibleSensorList;
 
+    resetTargetSdkVersionCache(opPackageName);
     bool isCapped = isRateCappedBasedOnPermission(opPackageName);
     for (size_t i = 0; i < initialSensorList.size(); i++) {
         Sensor sensor = initialSensorList[i];
@@ -1367,6 +1368,7 @@
     if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) {
         return nullptr;
     }
+    resetTargetSdkVersionCache(opPackageName);
 
     Mutex::Autolock _l(mLock);
     // To create a client in DATA_INJECTION mode to inject data, SensorService should already be
@@ -1402,6 +1404,7 @@
 sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
         const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
         const native_handle *resource) {
+    resetTargetSdkVersionCache(opPackageName);
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
 
     // No new direct connections are allowed when sensor privacy is enabled
@@ -1472,6 +1475,7 @@
     if (!clone) {
         return nullptr;
     }
+    native_handle_set_fdsan_tag(clone);
 
     sp<SensorDirectConnection> conn;
     SensorDevice& dev(SensorDevice::getInstance());
@@ -1485,7 +1489,7 @@
     }
 
     if (conn == nullptr) {
-        native_handle_close(clone);
+        native_handle_close_with_tag(clone);
         native_handle_delete(clone);
     } else {
         // add to list of direct connections
@@ -1643,14 +1647,6 @@
         checkWakeLockStateLocked(&connLock);
     }
 
-    {
-        Mutex::Autolock packageLock(sPackageTargetVersionLock);
-        auto iter = sPackageTargetVersion.find(c->mOpPackageName);
-        if (iter != sPackageTargetVersion.end()) {
-            sPackageTargetVersion.erase(iter);
-        }
-    }
-
     SensorDevice& dev(SensorDevice::getInstance());
     dev.notifyConnectionDestroyed(c);
 }
@@ -2091,6 +2087,14 @@
     return targetSdkVersion;
 }
 
+void SensorService::resetTargetSdkVersionCache(const String16& opPackageName) {
+    Mutex::Autolock packageLock(sPackageTargetVersionLock);
+    auto iter = sPackageTargetVersion.find(opPackageName);
+    if (iter != sPackageTargetVersion.end()) {
+        sPackageTargetVersion.erase(iter);
+    }
+}
+
 void SensorService::checkWakeLockState() {
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     checkWakeLockStateLocked(&connLock);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 234dc9c..4ba3c51 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -377,6 +377,7 @@
             const String16& opPackageName);
     static bool hasPermissionForSensor(const Sensor& sensor);
     static int getTargetSdkVersion(const String16& opPackageName);
+    static void resetTargetSdkVersionCache(const String16& opPackageName);
     // SensorService acquires a partial wakelock for delivering events from wake up sensors. This
     // method checks whether all the events from these wake up sensors have been delivered to the
     // corresponding applications, if yes the wakelock is released.
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index caf7f03..b00d1a7 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -89,6 +89,17 @@
 
     // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart
     printf("createInvalidDirectChannel=%d\n", ret);
+
+    // Secondary test: correct channel creation & destruction (should print 0)
+    ret = mgr.createDirectChannel(kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER,
+                                  resourceHandle);
+    printf("createValidDirectChannel=%d\n", ret);
+
+    // Third test: double-destroy (should not crash)
+    mgr.destroyDirectChannel(ret);
+    AHardwareBuffer_release(hardwareBuffer);
+    printf("duplicate destroyDirectChannel...\n");
+    mgr.destroyDirectChannel(ret);
 }
 
 int main() {
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 8d6a9bd..3de51a4 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -66,6 +66,61 @@
                 AStatsEvent_writeBool(event,
                     atomValue.get<VendorAtomValue::boolValue>());
                 break;
+            case VendorAtomValue::repeatedIntValue: {
+                const std::optional<std::vector<int>>& repeatedIntValue =
+                        atomValue.get<VendorAtomValue::repeatedIntValue>();
+                AStatsEvent_writeInt32Array(event, repeatedIntValue->data(),
+                                            repeatedIntValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedLongValue: {
+                const std::optional<std::vector<int64_t>>& repeatedLongValue =
+                        atomValue.get<VendorAtomValue::repeatedLongValue>();
+                AStatsEvent_writeInt64Array(event, repeatedLongValue->data(),
+                                            repeatedLongValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedFloatValue: {
+                const std::optional<std::vector<float>>& repeatedFloatValue =
+                        atomValue.get<VendorAtomValue::repeatedFloatValue>();
+                AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(),
+                                            repeatedFloatValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedStringValue: {
+                const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue =
+                        atomValue.get<VendorAtomValue::repeatedStringValue>();
+                const std::vector<std::optional<std::string>>& repeatedStringVector =
+                        *repeatedStringValue;
+                const char* cStringArray[repeatedStringVector.size()];
+
+                for (int i = 0; i < repeatedStringVector.size(); ++i) {
+                    cStringArray[i] = repeatedStringVector[i]->c_str();
+                }
+
+                AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size());
+                break;
+            }
+            case VendorAtomValue::repeatedBoolValue: {
+                const std::optional<std::vector<bool>>& repeatedBoolValue =
+                        atomValue.get<VendorAtomValue::repeatedBoolValue>();
+                const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue;
+                bool boolArray[repeatedBoolValue->size()];
+
+                for (int i = 0; i < repeatedBoolVector.size(); ++i) {
+                    boolArray[i] = repeatedBoolVector[i];
+                }
+
+                AStatsEvent_writeBoolArray(event, boolArray, repeatedBoolVector.size());
+                break;
+            }
+            case VendorAtomValue::byteArrayValue: {
+                const std::optional<std::vector<uint8_t>>& byteArrayValue =
+                        atomValue.get<VendorAtomValue::byteArrayValue>();
+
+                AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size());
+                break;
+            }
         }
     }
     AStatsEvent_build(event);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 76429ff..8a76d7c 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -25,6 +25,7 @@
 cc_defaults {
     name: "libsurfaceflinger_defaults",
     defaults: [
+        "android.hardware.graphics.composer3-ndk_shared",
         "surfaceflinger_defaults",
         "skia_renderengine_deps",
     ],
@@ -46,7 +47,6 @@
         "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-V2-cpp",
@@ -84,7 +84,6 @@
         "libserviceutils",
         "libshaders",
         "libtonemap",
-        "libtrace_proto",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
@@ -106,7 +105,6 @@
         "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",
@@ -142,17 +140,16 @@
     name: "libsurfaceflinger_sources",
     srcs: [
         "BackgroundExecutor.cpp",
-        "BufferStateLayer.cpp",
-        "ClientCache.cpp",
         "Client.cpp",
-        "EffectLayer.cpp",
+        "ClientCache.cpp",
+        "Display/DisplaySnapshot.cpp",
         "DisplayDevice.cpp",
         "DisplayHardware/AidlComposerHal.cpp",
-        "DisplayHardware/HidlComposerHal.cpp",
         "DisplayHardware/ComposerHal.cpp",
         "DisplayHardware/FramebufferSurface.cpp",
         "DisplayHardware/HWC2.cpp",
         "DisplayHardware/HWComposer.cpp",
+        "DisplayHardware/HidlComposerHal.cpp",
         "DisplayHardware/PowerAdvisor.cpp",
         "DisplayHardware/VirtualDisplaySurface.cpp",
         "DisplayRenderArea.cpp",
@@ -191,11 +188,11 @@
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceFlingerDefaultFactory.cpp",
-        "SurfaceInterceptor.cpp",
         "Tracing/LayerTracing.cpp",
         "Tracing/TransactionTracing.cpp",
         "Tracing/TransactionProtoParser.cpp",
         "TransactionCallbackInvoker.cpp",
+        "TransactionHandler.cpp",
         "TunnelModeEnabledReporter.cpp",
     ],
 }
@@ -226,7 +223,6 @@
     ],
     static_libs: [
         "libserviceutils",
-        "libtrace_proto",
     ],
 }
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
deleted file mode 100644
index 51a5445..0000000
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ /dev/null
@@ -1,1627 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "BufferStateLayer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "BufferStateLayer.h"
-
-#include <limits>
-
-#include <FrameTimeline/FrameTimeline.h>
-#include <compositionengine/CompositionEngine.h>
-#include <gui/BufferQueue.h>
-#include <private/gui/SyncFeatures.h>
-#include <renderengine/Image.h>
-#include "TunnelModeEnabledReporter.h"
-
-#include <gui/TraceUtils.h>
-#include "EffectLayer.h"
-#include "FrameTracer/FrameTracer.h"
-#include "TimeStats/TimeStats.h"
-
-#define EARLY_RELEASE_ENABLED false
-
-#include <compositionengine/LayerFECompositionState.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
-#include <cutils/compiler.h>
-#include <cutils/native_handle.h>
-#include <cutils/properties.h>
-#include <gui/BufferItem.h>
-#include <gui/BufferQueue.h>
-#include <gui/GLConsumer.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/Surface.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/DebugUtils.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/NativeHandle.h>
-#include <utils/StopWatch.h>
-#include <utils/Trace.h>
-
-#include <cmath>
-#include <cstdlib>
-#include <mutex>
-#include <sstream>
-
-#include "Colorizer.h"
-#include "DisplayDevice.h"
-#include "FrameTracer/FrameTracer.h"
-#include "TimeStats/TimeStats.h"
-
-namespace android {
-
-using PresentState = frametimeline::SurfaceFrame::PresentState;
-using gui::WindowInfo;
-namespace {
-static constexpr float defaultMaxLuminance = 1000.0;
-
-constexpr mat4 inverseOrientation(uint32_t transform) {
-    const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
-    const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
-    const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
-    mat4 tr;
-
-    if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
-        tr = tr * rot90;
-    }
-    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
-        tr = tr * flipH;
-    }
-    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
-        tr = tr * flipV;
-    }
-    return inverse(tr);
-}
-
-bool assignTransform(ui::Transform* dst, ui::Transform& from) {
-    if (*dst == from) {
-        return false;
-    }
-    *dst = from;
-    return true;
-}
-
-TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
-    using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
-    using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
-    const auto frameRateCompatibility = [frameRate] {
-        switch (frameRate.type) {
-            case Layer::FrameRateCompatibility::Default:
-                return FrameRateCompatibility::Default;
-            case Layer::FrameRateCompatibility::ExactOrMultiple:
-                return FrameRateCompatibility::ExactOrMultiple;
-            default:
-                return FrameRateCompatibility::Undefined;
-        }
-    }();
-
-    const auto seamlessness = [frameRate] {
-        switch (frameRate.seamlessness) {
-            case scheduler::Seamlessness::OnlySeamless:
-                return Seamlessness::ShouldBeSeamless;
-            case scheduler::Seamlessness::SeamedAndSeamless:
-                return Seamlessness::NotRequired;
-            default:
-                return Seamlessness::Undefined;
-        }
-    }();
-
-    return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
-                                       .frameRateCompatibility = frameRateCompatibility,
-                                       .seamlessness = seamlessness};
-}
-} // namespace
-
-BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
-      : Layer(args),
-        mTextureName(args.textureName),
-        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()},
-        mHwcSlotGenerator(sp<HwcSlotGenerator>::make()) {
-    ALOGV("Creating Layer %s", getDebugName());
-
-    mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
-    mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
-    mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
-    mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
-}
-
-BufferStateLayer::~BufferStateLayer() {
-    // The original layer and the clone layer share the same texture and buffer. Therefore, only
-    // one of the layers, in this case the original layer, needs to handle the deletion. The
-    // original layer and the clone should be removed at the same time so there shouldn't be any
-    // issue with the clone layer trying to use the texture.
-    if (mBufferInfo.mBuffer != nullptr) {
-        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                  mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
-                                  mBufferInfo.mFence,
-                                  mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
-                                          mOwnerUid));
-    }
-    if (!isClone()) {
-        // The original layer and the clone layer share the same texture. Therefore, only one of
-        // the layers, in this case the original layer, needs to handle the deletion. The original
-        // layer and the clone should be removed at the same time so there shouldn't be any issue
-        // with the clone layer trying to use the deleted texture.
-        mFlinger->deleteTextureAsync(mTextureName);
-    }
-    const int32_t layerId = getSequence();
-    mFlinger->mTimeStats->onDestroy(layerId);
-    mFlinger->mFrameTracer->onDestroy(layerId);
-}
-
-void BufferStateLayer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
-                                                 const sp<GraphicBuffer>& buffer,
-                                                 uint64_t framenumber,
-                                                 const sp<Fence>& releaseFence,
-                                                 uint32_t currentMaxAcquiredBufferCount) {
-    if (!listener) {
-        return;
-    }
-    ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
-    listener->onReleaseBuffer({buffer->getId(), framenumber},
-                              releaseFence ? releaseFence : Fence::NO_FENCE,
-                              currentMaxAcquiredBufferCount);
-}
-
-// -----------------------------------------------------------------------
-// Interface implementation for Layer
-// -----------------------------------------------------------------------
-void BufferStateLayer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
-    // If we are displayed on multiple displays in a single composition cycle then we would
-    // need to do careful tracking to enable the use of the mLastClientCompositionFence.
-    //  For example we can only use it if all the displays are client comp, and we need
-    //  to merge all the client comp fences. We could do this, but for now we just
-    // disable the optimization when a layer is composed on multiple displays.
-    if (mClearClientCompositionFenceOnLayerDisplayed) {
-        mLastClientCompositionFence = nullptr;
-    } else {
-        mClearClientCompositionFenceOnLayerDisplayed = 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
-    // let the client know when that previous buffer is removed from the screen.
-    //
-    // Every other transaction on this layer does not need a release fence because no other
-    // Transactions that were set on this layer this frame are going to have their preceeding buffer
-    // removed from the display this frame.
-    //
-    // For example, if we have 3 transactions this frame. The first transaction doesn't contain a
-    // buffer so it doesn't need a previous release fence because the layer still needs the previous
-    // buffer. The second transaction contains a buffer so it needs a previous release fence because
-    // the previous buffer will be released this frame. The third transaction also contains a
-    // buffer. It replaces the buffer in the second transaction. The buffer in the second
-    // transaction will now no longer be presented so it is released immediately and the third
-    // transaction doesn't need a previous release fence.
-    sp<CallbackHandle> ch;
-    for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer &&
-            mDrawingState.releaseBufferEndpoint == handle->listener) {
-            ch = handle;
-            break;
-        }
-    }
-
-    // Prevent tracing the same release multiple times.
-    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
-        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
-    }
-
-    if (ch != nullptr) {
-        ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
-        ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
-        ch->name = mName;
-    }
-}
-
-void BufferStateLayer::onSurfaceFrameCreated(
-        const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
-    while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
-        // Too many SurfaceFrames pending classification. The front of the deque is probably not
-        // tracked by FrameTimeline and will never be presented. This will only result in a memory
-        // leak.
-        ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
-              mName.c_str());
-        std::string miniDump = mPendingJankClassifications.front()->miniDump();
-        ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
-        mPendingJankClassifications.pop_front();
-    }
-    mPendingJankClassifications.emplace_back(surfaceFrame);
-}
-
-void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
-    for (const auto& handle : mDrawingState.callbackHandles) {
-        handle->transformHint = mTransformHint;
-        handle->dequeueReadyTime = dequeueReadyTime;
-        handle->currentMaxAcquiredBufferCount =
-                mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
-        ATRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(),
-                              handle->previousReleaseCallbackId.framenumber);
-    }
-
-    for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer &&
-            mDrawingState.releaseBufferEndpoint == handle->listener) {
-            handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
-            break;
-        }
-    }
-
-    std::vector<JankData> jankData;
-    jankData.reserve(mPendingJankClassifications.size());
-    while (!mPendingJankClassifications.empty()
-            && mPendingJankClassifications.front()->getJankType()) {
-        std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
-                mPendingJankClassifications.front();
-        mPendingJankClassifications.pop_front();
-        jankData.emplace_back(
-                JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
-    }
-
-    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 = {};
-}
-
-bool BufferStateLayer::willPresentCurrentTransaction() const {
-    // Returns true if the most recent Transaction applied to CurrentState will be presented.
-    return (getSidebandStreamChanged() || getAutoRefresh() ||
-            (mDrawingState.modified &&
-             (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr)));
-}
-
-Rect BufferStateLayer::getCrop(const Layer::State& s) const {
-    return s.crop;
-}
-
-bool BufferStateLayer::setTransform(uint32_t transform) {
-    if (mDrawingState.bufferTransform == transform) return false;
-    mDrawingState.bufferTransform = transform;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInverse) {
-    if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false;
-    mDrawingState.sequence++;
-    mDrawingState.transformToDisplayInverse = transformToDisplayInverse;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setCrop(const Rect& crop) {
-    if (mDrawingState.crop == crop) return false;
-    mDrawingState.sequence++;
-    mDrawingState.crop = crop;
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) {
-    if (mDrawingState.bufferCrop == bufferCrop) return false;
-
-    mDrawingState.sequence++;
-    mDrawingState.bufferCrop = bufferCrop;
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setDestinationFrame(const Rect& destinationFrame) {
-    if (mDrawingState.destinationFrame == destinationFrame) return false;
-
-    mDrawingState.sequence++;
-    mDrawingState.destinationFrame = destinationFrame;
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-// Translate destination frame into scale and position. If a destination frame is not set, use the
-// provided scale and position
-bool BufferStateLayer::updateGeometry() {
-    if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
-        mDrawingState.destinationFrame.isEmpty()) {
-        // If destination frame is not set, use the requested transform set via
-        // BufferStateLayer::setPosition and BufferStateLayer::setMatrix.
-        return assignTransform(&mDrawingState.transform, mRequestedTransform);
-    }
-
-    Rect destRect = mDrawingState.destinationFrame;
-    int32_t destW = destRect.width();
-    int32_t destH = destRect.height();
-    if (destRect.left < 0) {
-        destRect.left = 0;
-        destRect.right = destW;
-    }
-    if (destRect.top < 0) {
-        destRect.top = 0;
-        destRect.bottom = destH;
-    }
-
-    if (!mDrawingState.buffer) {
-        ui::Transform t;
-        t.set(destRect.left, destRect.top);
-        return assignTransform(&mDrawingState.transform, t);
-    }
-
-    uint32_t bufferWidth = mDrawingState.buffer->getWidth();
-    uint32_t bufferHeight = mDrawingState.buffer->getHeight();
-    // Undo any transformations on the buffer.
-    if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
-        std::swap(bufferWidth, bufferHeight);
-    }
-    uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-    if (mDrawingState.transformToDisplayInverse) {
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufferWidth, bufferHeight);
-        }
-    }
-
-    float sx = destW / static_cast<float>(bufferWidth);
-    float sy = destH / static_cast<float>(bufferHeight);
-    ui::Transform t;
-    t.set(sx, 0, 0, sy);
-    t.set(destRect.left, destRect.top);
-    return assignTransform(&mDrawingState.transform, t);
-}
-
-bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix) {
-    if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
-        mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
-        return false;
-    }
-
-    ui::Transform t;
-    t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-
-    mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-
-    mDrawingState.sequence++;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    return true;
-}
-
-bool BufferStateLayer::setPosition(float x, float y) {
-    if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
-        return false;
-    }
-
-    mRequestedTransform.set(x, y);
-
-    mDrawingState.sequence++;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    return true;
-}
-
-bool BufferStateLayer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
-                                 const BufferData& bufferData, nsecs_t postTime,
-                                 nsecs_t desiredPresentTime, bool isAutoTimestamp,
-                                 std::optional<nsecs_t> dequeueTime,
-                                 const FrameTimelineInfo& info) {
-    ATRACE_CALL();
-
-    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 (!mBufferInfo.mBuffer ||
-            (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
-             mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) {
-            // If mDrawingState has a buffer, and we are about to update again
-            // before swapping to drawing state, then the first buffer will be
-            // dropped and we should decrement the pending buffer count and
-            // call any release buffer callbacks if set.
-            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mDrawingState.acquireFence,
-                                      mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
-                                              mOwnerUid));
-            decrementPendingBufferCount();
-            if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
-                mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
-              addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
-              mDrawingState.bufferSurfaceFrameTX.reset();
-            }
-        } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
-            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mLastClientCompositionFence,
-                                      mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
-                                              mOwnerUid));
-            mLastClientCompositionFence = nullptr;
-        }
-    }
-
-    mDrawingState.frameNumber = frameNumber;
-    mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
-    mDrawingState.buffer = std::move(buffer);
-    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);
-    if (mDrawingState.acquireFenceTime->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
-        // We latched this buffer unsiganled, so we need to pass the acquire fence
-        // on the callback instead of just the acquire time, since it's unknown at
-        // this point.
-        mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFence;
-    } else {
-        mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
-    }
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    const int32_t layerId = getSequence();
-    mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
-                                      mOwnerUid, postTime, getGameMode());
-    mDrawingState.desiredPresentTime = desiredPresentTime;
-    mDrawingState.isAutoTimestamp = isAutoTimestamp;
-
-    const nsecs_t presentTime = [&] {
-        if (!isAutoTimestamp) return desiredPresentTime;
-
-        const auto prediction =
-                mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
-        if (prediction.has_value()) return prediction->presentTime;
-
-        return static_cast<nsecs_t>(0);
-    }();
-
-    using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
-    mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
-
-    setFrameTimelineVsyncForBufferTransaction(info, postTime);
-
-    if (dequeueTime && *dequeueTime != 0) {
-        const uint64_t bufferId = mDrawingState.buffer->getId();
-        mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
-        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
-                                               FrameTracer::FrameEvent::DEQUEUE);
-        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
-                                               FrameTracer::FrameEvent::QUEUE);
-    }
-
-    mDrawingState.width = mDrawingState.buffer->getWidth();
-    mDrawingState.height = mDrawingState.buffer->getHeight();
-    mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
-    return true;
-}
-
-bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) {
-    if (mDrawingState.dataspace == dataspace) return false;
-    mDrawingState.dataspace = dataspace;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
-    if (mDrawingState.hdrMetadata == hdrMetadata) return false;
-    mDrawingState.hdrMetadata = hdrMetadata;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) {
-    mDrawingState.surfaceDamageRegion = surfaceDamage;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setApi(int32_t api) {
-    if (mDrawingState.api == api) return false;
-    mDrawingState.api = api;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
-    if (mDrawingState.sidebandStream == sidebandStream) return false;
-
-    if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
-        mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
-    } else if (sidebandStream != nullptr) {
-        mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount();
-    }
-
-    mDrawingState.sidebandStream = sidebandStream;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    if (!mSidebandStreamChanged.exchange(true)) {
-        // mSidebandStreamChanged was false
-        mFlinger->onLayerUpdate();
-    }
-    return true;
-}
-
-bool BufferStateLayer::setTransactionCompletedListeners(
-        const std::vector<sp<CallbackHandle>>& handles) {
-    // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
-    if (handles.empty()) {
-        mReleasePreviousBuffer = false;
-        return false;
-    }
-
-    const bool willPresent = willPresentCurrentTransaction();
-
-    for (const auto& handle : handles) {
-        // If this transaction set a buffer on this layer, release its previous buffer
-        handle->releasePreviousBuffer = mReleasePreviousBuffer;
-
-        // If this layer will be presented in this frame
-        if (willPresent) {
-            // If this transaction set an acquire fence on this layer, set its acquire time
-            handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
-            handle->frameNumber = mDrawingState.frameNumber;
-
-            // Store so latched time and release fence can be set
-            mDrawingState.callbackHandles.push_back(handle);
-
-        } else { // If this layer will NOT need to be relatched and presented this frame
-            // Notify the transaction completed thread this handle is done
-            mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
-        }
-    }
-
-    mReleasePreviousBuffer = false;
-    mCallbackHandleAcquireTimeOrFence = -1;
-
-    return willPresent;
-}
-
-bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
-    mDrawingState.sequence++;
-    mDrawingState.transparentRegionHint = transparent;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-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) {
-        return Rect::INVALID_RECT;
-    }
-
-    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-
-    // Undo any transformations on the buffer and return the result.
-    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
-        std::swap(bufWidth, bufHeight);
-    }
-
-    if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufWidth, bufHeight);
-        }
-    }
-
-    return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
-}
-
-FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
-    if (mBufferInfo.mBuffer == nullptr) {
-        return parentBounds;
-    }
-
-    return getBufferSize(getDrawingState()).toFloatRect();
-}
-
-// -----------------------------------------------------------------------
-bool BufferStateLayer::fenceHasSignaled() const {
-    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
-        return true;
-    }
-
-    const bool fenceSignaled =
-            getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
-    if (!fenceSignaled) {
-        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
-                                                    TimeStats::LatchSkipReason::LateAcquire);
-    }
-
-    return fenceSignaled;
-}
-
-bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
-    for (const auto& handle : mDrawingState.callbackHandles) {
-        handle->refreshStartTime = refreshStartTime;
-    }
-    return hasReadyFrame();
-}
-
-void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
-    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.
-    editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
-
-    if (mSidebandStreamChanged.exchange(false)) {
-        const State& s(getDrawingState());
-        // mSidebandStreamChanged was true
-        mSidebandStream = s.sidebandStream;
-        editCompositionState()->sidebandStream = mSidebandStream;
-        if (mSidebandStream != nullptr) {
-            setTransactionFlags(eTransactionNeeded);
-            mFlinger->setTransactionFlags(eTraversalNeeded);
-        }
-        recomputeVisibleRegions = true;
-
-        return true;
-    }
-    return false;
-}
-
-bool BufferStateLayer::hasFrameUpdate() const {
-    const State& c(getDrawingState());
-    return (mDrawingStateModified || mDrawingState.modified) && (c.buffer != nullptr || c.bgColorLayer != nullptr);
-}
-
-void BufferStateLayer::updateTexImage(nsecs_t latchTime) {
-    const State& s(getDrawingState());
-
-    if (!s.buffer) {
-        if (s.bgColorLayer) {
-            for (auto& handle : mDrawingState.callbackHandles) {
-                handle->latchTime = latchTime;
-            }
-        }
-        return;
-    }
-
-    for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->frameNumber == mDrawingState.frameNumber) {
-            handle->latchTime = latchTime;
-        }
-    }
-
-    const int32_t layerId = getSequence();
-    const uint64_t bufferId = mDrawingState.buffer->getId();
-    const uint64_t frameNumber = mDrawingState.frameNumber;
-    const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
-    mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
-    mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime);
-
-    mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence,
-                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime,
-                                           FrameTracer::FrameEvent::LATCH);
-
-    auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
-    if (bufferSurfaceFrame != nullptr &&
-        bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
-        // Update only if the bufferSurfaceFrame wasn't already presented. A Presented
-        // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
-        // are processing the next state.
-        addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
-                                          mDrawingState.acquireFenceTime->getSignalTime(),
-                                          latchTime);
-        mDrawingState.bufferSurfaceFrameTX.reset();
-    }
-
-    std::deque<sp<CallbackHandle>> remainingHandles;
-    mFlinger->getTransactionCallbackInvoker()
-            .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
-    mDrawingState.callbackHandles = remainingHandles;
-
-    mDrawingStateModified = false;
-}
-
-void BufferStateLayer::gatherBufferInfo() {
-    if (!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
-        decrementPendingBufferCount();
-    }
-
-    mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
-    mBufferInfo.mBuffer = mDrawingState.buffer;
-    mBufferInfo.mFence = mDrawingState.acquireFence;
-    mBufferInfo.mFrameNumber = mDrawingState.frameNumber;
-    mBufferInfo.mPixelFormat =
-            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
-    mBufferInfo.mFrameLatencyNeeded = true;
-    mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime;
-    mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
-    mBufferInfo.mFence = mDrawingState.acquireFence;
-    mBufferInfo.mTransform = mDrawingState.bufferTransform;
-    auto lastDataspace = mBufferInfo.mDataspace;
-    mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
-    if (lastDataspace != mBufferInfo.mDataspace) {
-        mFlinger->mSomeDataspaceChanged = true;
-    }
-    mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
-    mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
-    mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion;
-    mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
-    mBufferInfo.mApi = mDrawingState.api;
-    mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
-    mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(mDrawingState.clientCacheId);
-}
-
-Rect BufferStateLayer::computeBufferCrop(const State& s) {
-    if (s.buffer && !s.bufferCrop.isEmpty()) {
-        Rect bufferCrop;
-        s.buffer->getBounds().intersect(s.bufferCrop, &bufferCrop);
-        return bufferCrop;
-    } else if (s.buffer) {
-        return s.buffer->getBounds();
-    } else {
-        return s.bufferCrop;
-    }
-}
-
-sp<Layer> BufferStateLayer::createClone() {
-    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
-    args.textureName = mTextureName;
-    sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
-    layer->mHwcSlotGenerator = mHwcSlotGenerator;
-    layer->setInitialValuesForClone(sp<Layer>::fromExisting(this));
-    return layer;
-}
-
-bool BufferStateLayer::bufferNeedsFiltering() const {
-    const State& s(getDrawingState());
-    if (!s.buffer) {
-        return false;
-    }
-
-    int32_t bufferWidth = static_cast<int32_t>(s.buffer->getWidth());
-    int32_t bufferHeight = static_cast<int32_t>(s.buffer->getHeight());
-
-    // Undo any transformations on the buffer and return the result.
-    if (s.bufferTransform & ui::Transform::ROT_90) {
-        std::swap(bufferWidth, bufferHeight);
-    }
-
-    if (s.transformToDisplayInverse) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufferWidth, bufferHeight);
-        }
-    }
-
-    const Rect layerSize{getBounds()};
-    int32_t layerWidth = layerSize.getWidth();
-    int32_t layerHeight = layerSize.getHeight();
-
-    // Align the layer orientation with the buffer before comparism
-    if (mTransformHint & ui::Transform::ROT_90) {
-        std::swap(layerWidth, layerHeight);
-    }
-
-    return layerWidth != bufferWidth || layerHeight != bufferHeight;
-}
-
-void BufferStateLayer::decrementPendingBufferCount() {
-    int32_t pendingBuffers = --mPendingBufferTransactions;
-    tracePendingBufferCount(pendingBuffers);
-}
-
-void BufferStateLayer::tracePendingBufferCount(int32_t pendingBuffers) {
-    ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
-}
-
-
-/*
- * We don't want to send the layer's transform to input, but rather the
- * parent's transform. This is because BufferStateLayer's transform is
- * information about how the buffer is placed on screen. The parent's
- * transform makes more sense to send since it's information about how the
- * layer is placed on screen. This transform is used by input to determine
- * how to go from screen space back to window space.
- */
-ui::Transform BufferStateLayer::getInputTransform() const {
-    sp<Layer> parent = mDrawingParent.promote();
-    if (parent == nullptr) {
-        return ui::Transform();
-    }
-
-    return parent->getTransform();
-}
-
-/**
- * Similar to getInputTransform, we need to update the bounds to include the transform.
- * This is because bounds for BSL doesn't include buffer transform, where the input assumes
- * that's already included.
- */
-Rect BufferStateLayer::getInputBounds() const {
-    Rect bufferBounds = getCroppedBufferSize(getDrawingState());
-    if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) {
-        return bufferBounds;
-    }
-    return mDrawingState.transform.transform(bufferBounds);
-}
-
-bool BufferStateLayer::simpleBufferUpdate(const layer_state_t& s) const {
-    const uint64_t requiredFlags = layer_state_t::eBufferChanged;
-
-    const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
-            layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
-            layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
-            layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged |
-            layer_state_t::eReparent;
-
-    const uint64_t allowedFlags = layer_state_t::eHasListenerCallbacksChanged |
-            layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFrameRateChanged |
-            layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eApiChanged |
-            layer_state_t::eMetadataChanged | layer_state_t::eDropInputModeChanged |
-            layer_state_t::eInputInfoChanged;
-
-    if ((s.what & requiredFlags) != requiredFlags) {
-        ALOGV("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
-              (s.what | requiredFlags) & ~s.what);
-        return false;
-    }
-
-    if (s.what & deniedFlags) {
-        ALOGV("%s: false [has denied flags 0x%" PRIx64 "]", __func__, s.what & deniedFlags);
-        return false;
-    }
-
-    if (s.what & allowedFlags) {
-        ALOGV("%s: [has allowed flags 0x%" PRIx64 "]", __func__, s.what & allowedFlags);
-    }
-
-    if (s.what & layer_state_t::ePositionChanged) {
-        if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) {
-            ALOGV("%s: false [ePositionChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eAlphaChanged) {
-        if (mDrawingState.color.a != s.alpha) {
-            ALOGV("%s: false [eAlphaChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eColorTransformChanged) {
-        if (mDrawingState.colorTransform != s.colorTransform) {
-            ALOGV("%s: false [eColorTransformChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eBackgroundColorChanged) {
-        if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) {
-            ALOGV("%s: false [eBackgroundColorChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eMatrixChanged) {
-        if (mRequestedTransform.dsdx() != s.matrix.dsdx ||
-            mRequestedTransform.dtdy() != s.matrix.dtdy ||
-            mRequestedTransform.dtdx() != s.matrix.dtdx ||
-            mRequestedTransform.dsdy() != s.matrix.dsdy) {
-            ALOGV("%s: false [eMatrixChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eCornerRadiusChanged) {
-        if (mDrawingState.cornerRadius != s.cornerRadius) {
-            ALOGV("%s: false [eCornerRadiusChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) {
-        if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) {
-            ALOGV("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eTransformChanged) {
-        if (mDrawingState.bufferTransform != s.transform) {
-            ALOGV("%s: false [eTransformChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eTransformToDisplayInverseChanged) {
-        if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) {
-            ALOGV("%s: false [eTransformToDisplayInverseChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eCropChanged) {
-        if (mDrawingState.crop != s.crop) {
-            ALOGV("%s: false [eCropChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eDataspaceChanged) {
-        if (mDrawingState.dataspace != s.dataspace) {
-            ALOGV("%s: false [eDataspaceChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eHdrMetadataChanged) {
-        if (mDrawingState.hdrMetadata != s.hdrMetadata) {
-            ALOGV("%s: false [eHdrMetadataChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eSidebandStreamChanged) {
-        if (mDrawingState.sidebandStream != s.sidebandStream) {
-            ALOGV("%s: false [eSidebandStreamChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eColorSpaceAgnosticChanged) {
-        if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) {
-            ALOGV("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eShadowRadiusChanged) {
-        if (mDrawingState.shadowRadius != s.shadowRadius) {
-            ALOGV("%s: false [eShadowRadiusChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eFixedTransformHintChanged) {
-        if (mDrawingState.fixedTransformHint != s.fixedTransformHint) {
-            ALOGV("%s: false [eFixedTransformHintChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eTrustedOverlayChanged) {
-        if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) {
-            ALOGV("%s: false [eTrustedOverlayChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eStretchChanged) {
-        StretchEffect temp = s.stretchEffect;
-        temp.sanitize();
-        if (mDrawingState.stretchEffect != temp) {
-            ALOGV("%s: false [eStretchChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eBufferCropChanged) {
-        if (mDrawingState.bufferCrop != s.bufferCrop) {
-            ALOGV("%s: false [eBufferCropChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eDestinationFrameChanged) {
-        if (mDrawingState.destinationFrame != s.destinationFrame) {
-            ALOGV("%s: false [eDestinationFrameChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eDimmingEnabledChanged) {
-        if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
-            ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    ALOGV("%s: true", __func__);
-    return true;
-}
-
-void BufferStateLayer::useSurfaceDamage() {
-    if (mFlinger->mForceFullDamage) {
-        surfaceDamageRegion = Region::INVALID_REGION;
-    } else {
-        surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
-    }
-}
-
-void BufferStateLayer::useEmptyDamage() {
-    surfaceDamageRegion.clear();
-}
-
-bool BufferStateLayer::isOpaque(const Layer::State& s) const {
-    // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
-    // layer's opaque flag.
-    if ((mSidebandStream == nullptr) && (mBufferInfo.mBuffer == nullptr)) {
-        return false;
-    }
-
-    // if the layer has the opaque flag, then we're always opaque,
-    // otherwise we use the current buffer's format.
-    return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat());
-}
-
-bool BufferStateLayer::canReceiveInput() const {
-    return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
-}
-
-bool BufferStateLayer::isVisible() const {
-    return !isHiddenByPolicy() && getAlpha() > 0.0f &&
-            (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
-}
-
-std::optional<compositionengine::LayerFE::LayerSettings> BufferStateLayer::prepareClientComposition(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
-    ATRACE_CALL();
-
-    std::optional<compositionengine::LayerFE::LayerSettings> result =
-            Layer::prepareClientComposition(targetSettings);
-    if (!result) {
-        return result;
-    }
-
-    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);
-    const bool bufferCanBeUsedAsHwTexture =
-            mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
-    compositionengine::LayerFE::LayerSettings& layer = *result;
-    if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
-        ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
-                 mName.c_str());
-        prepareClearClientComposition(layer, true /* blackout */);
-        return layer;
-    }
-
-    const State& s(getDrawingState());
-    layer.source.buffer.buffer = mBufferInfo.mBuffer;
-    layer.source.buffer.isOpaque = isOpaque(s);
-    layer.source.buffer.fence = mBufferInfo.mFence;
-    layer.source.buffer.textureName = mTextureName;
-    layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
-    layer.source.buffer.isY410BT2020 = isHdrY410();
-    bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
-    bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
-    float maxLuminance = 0.f;
-    if (hasSmpte2086 && hasCta861_3) {
-        maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
-                                mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
-    } else if (hasSmpte2086) {
-        maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
-    } else if (hasCta861_3) {
-        maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
-    } else {
-        switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
-            case HAL_DATASPACE_TRANSFER_ST2084:
-            case HAL_DATASPACE_TRANSFER_HLG:
-                // Behavior-match previous releases for HDR content
-                maxLuminance = defaultMaxLuminance;
-                break;
-        }
-    }
-    layer.source.buffer.maxLuminanceNits = maxLuminance;
-    layer.frameNumber = mCurrentFrameNumber;
-    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
-
-    const bool useFiltering =
-            targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
-
-    // Query the texture matrix given our current filtering mode.
-    float textureMatrix[16];
-    getDrawingTransformMatrix(useFiltering, textureMatrix);
-
-    if (getTransformToDisplayInverse()) {
-        /*
-         * the code below applies the primary display's inverse transform to
-         * the texture transform
-         */
-        uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        mat4 tr = inverseOrientation(transform);
-
-        /**
-         * TODO(b/36727915): This is basically a hack.
-         *
-         * Ensure that regardless of the parent transformation,
-         * this buffer is always transformed from native display
-         * orientation to display orientation. For example, in the case
-         * of a camera where the buffer remains in native orientation,
-         * we want the pixels to always be upright.
-         */
-        sp<Layer> p = mDrawingParent.promote();
-        if (p != nullptr) {
-            const auto parentTransform = p->getTransform();
-            tr = tr * inverseOrientation(parentTransform.getOrientation());
-        }
-
-        // and finally apply it to the original texture matrix
-        const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
-        memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
-    }
-
-    const Rect win{getBounds()};
-    float bufferWidth = getBufferSize(s).getWidth();
-    float bufferHeight = getBufferSize(s).getHeight();
-
-    // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
-    // been set and there is no parent layer bounds. In that case, the scale is meaningless so
-    // ignore them.
-    if (!getBufferSize(s).isValid()) {
-        bufferWidth = float(win.right) - float(win.left);
-        bufferHeight = float(win.bottom) - float(win.top);
-    }
-
-    const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
-    const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
-    const float translateY = float(win.top) / bufferHeight;
-    const float translateX = float(win.left) / bufferWidth;
-
-    // Flip y-coordinates because GLConsumer expects OpenGL convention.
-    mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
-            mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
-            mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
-            mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
-
-    layer.source.buffer.useTextureFiltering = useFiltering;
-    layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
-
-    return layer;
-}
-
-bool BufferStateLayer::isHdrY410() const {
-    // pixel format is HDR Y410 masquerading as RGBA_1010102
-    return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
-            mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
-            mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
-}
-
-sp<compositionengine::LayerFE> BufferStateLayer::getCompositionEngineLayerFE() const {
-    return asLayerFE();
-}
-
-compositionengine::LayerFECompositionState* BufferStateLayer::editCompositionState() {
-    return mCompositionState.get();
-}
-
-const compositionengine::LayerFECompositionState* BufferStateLayer::getCompositionState() const {
-    return mCompositionState.get();
-}
-
-void BufferStateLayer::preparePerFrameCompositionState() {
-    Layer::preparePerFrameCompositionState();
-
-    // Sideband layers
-    auto* compositionState = editCompositionState();
-    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
-                ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
-                : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
-    }
-
-    compositionState->buffer = getBuffer();
-    compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
-            ? 0
-            : mBufferInfo.mBufferSlot;
-    compositionState->acquireFence = mBufferInfo.mFence;
-    compositionState->frameNumber = mBufferInfo.mFrameNumber;
-    compositionState->sidebandStreamHasFrame = false;
-}
-
-void BufferStateLayer::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;
-
-    for (const auto& handle : mDrawingState.callbackHandles) {
-        handle->gpuCompositionDoneFence = glDoneFence;
-        handle->compositorTiming = compositorTiming;
-    }
-
-    // Update mFrameTracker.
-    nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
-    mFrameTracker.setDesiredPresentTime(desiredPresentTime);
-
-    const int32_t layerId = getSequence();
-    mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
-
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    if (outputLayer && outputLayer->requiresClientComposition()) {
-        nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
-        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
-                                               clientCompositionTimestamp,
-                                               FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
-        // Update the SurfaceFrames in the drawing state
-        if (mDrawingState.bufferSurfaceFrameTX) {
-            mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
-        }
-        for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
-            surfaceFrame->setGpuComposition();
-        }
-    }
-
-    std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
-    if (frameReadyFence->isValid()) {
-        mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
-    } else {
-        // There was no fence for this frame, so assume that it was ready
-        // to be presented at the desired present time.
-        mFrameTracker.setFrameReadyTime(desiredPresentTime);
-    }
-
-    if (display) {
-        const Fps refreshRate = display->refreshRateConfigs().getActiveMode()->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, vote, gameMode);
-            mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
-                                               presentFence,
-                                               FrameTracer::FrameEvent::PRESENT_FENCE);
-            mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
-        } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
-                   displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
-            // The HWC doesn't support present fences, so use the refresh
-            // timestamp instead.
-            const nsecs_t actualPresentTime = display->getRefreshTimestamp();
-            mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
-                                                 refreshRate, renderRate, vote, gameMode);
-            mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
-                                                   mCurrentFrameNumber, actualPresentTime,
-                                                   FrameTracer::FrameEvent::PRESENT_FENCE);
-            mFrameTracker.setActualPresentTime(actualPresentTime);
-        }
-    }
-
-    mFrameTracker.advanceFrame();
-    mBufferInfo.mFrameLatencyNeeded = false;
-}
-
-bool BufferStateLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
-    ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
-                          getDrawingState().frameNumber);
-
-    bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
-
-    if (refreshRequired) {
-        return refreshRequired;
-    }
-
-    // If the head buffer's acquire fence hasn't signaled yet, return and
-    // try again later
-    if (!fenceHasSignaled()) {
-        ATRACE_NAME("!fenceHasSignaled()");
-        mFlinger->onLayerUpdate();
-        return false;
-    }
-
-    updateTexImage(latchTime);
-    if (mDrawingState.buffer == nullptr) {
-        return false;
-    }
-
-    // Capture the old state of the layer for comparisons later
-    BufferInfo oldBufferInfo = mBufferInfo;
-    const bool oldOpacity = isOpaque(mDrawingState);
-    mPreviousFrameNumber = mCurrentFrameNumber;
-    mCurrentFrameNumber = mDrawingState.frameNumber;
-    gatherBufferInfo();
-
-    if (oldBufferInfo.mBuffer == nullptr) {
-        // the first time we receive a buffer, we need to trigger a
-        // geometry invalidation.
-        recomputeVisibleRegions = true;
-    }
-
-    if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
-        (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
-        (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
-        (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
-        recomputeVisibleRegions = true;
-    }
-
-    if (oldBufferInfo.mBuffer != nullptr) {
-        uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-        uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-        if (bufWidth != oldBufferInfo.mBuffer->getWidth() ||
-            bufHeight != oldBufferInfo.mBuffer->getHeight()) {
-            recomputeVisibleRegions = true;
-        }
-    }
-
-    if (oldOpacity != isOpaque(mDrawingState)) {
-        recomputeVisibleRegions = true;
-    }
-
-    return true;
-}
-
-bool BufferStateLayer::hasReadyFrame() const {
-    return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
-}
-
-bool BufferStateLayer::isProtected() const {
-    return (mBufferInfo.mBuffer != nullptr) &&
-            (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-}
-
-// As documented in libhardware header, formats in the range
-// 0x100 - 0x1FF are specific to the HAL implementation, and
-// are known to have no alpha channel
-// TODO: move definition for device-specific range into
-// hardware.h, instead of using hard-coded values here.
-#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
-
-bool BufferStateLayer::getOpacityForFormat(PixelFormat format) {
-    if (HARDWARE_IS_DEVICE_FORMAT(format)) {
-        return true;
-    }
-    switch (format) {
-        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)
-    return true;
-}
-
-bool BufferStateLayer::needsFiltering(const DisplayDevice* display) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    if (outputLayer == nullptr) {
-        return false;
-    }
-
-    // We need filtering if the sourceCrop rectangle size does not match the
-    // displayframe rectangle size (not a 1:1 render)
-    const auto& compositionState = outputLayer->getState();
-    const auto displayFrame = compositionState.displayFrame;
-    const auto sourceCrop = compositionState.sourceCrop;
-    return sourceCrop.getHeight() != displayFrame.getHeight() ||
-            sourceCrop.getWidth() != displayFrame.getWidth();
-}
-
-bool BufferStateLayer::needsFilteringForScreenshots(
-        const DisplayDevice* display, const ui::Transform& inverseParentTransform) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    if (outputLayer == nullptr) {
-        return false;
-    }
-
-    // We need filtering if the sourceCrop rectangle size does not match the
-    // viewport rectangle size (not a 1:1 render)
-    const auto& compositionState = outputLayer->getState();
-    const ui::Transform& displayTransform = display->getTransform();
-    const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
-    // Undo the transformation of the displayFrame so that we're back into
-    // layer-stack space.
-    const Rect frame = inverseTransform.transform(compositionState.displayFrame);
-    const FloatRect sourceCrop = compositionState.sourceCrop;
-
-    int32_t frameHeight = frame.getHeight();
-    int32_t frameWidth = frame.getWidth();
-    // If the display transform had a rotational component then undo the
-    // rotation so that the orientation matches the source crop.
-    if (displayTransform.getOrientation() & ui::Transform::ROT_90) {
-        std::swap(frameHeight, frameWidth);
-    }
-    return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
-}
-
-void BufferStateLayer::latchAndReleaseBuffer() {
-    if (hasReadyFrame()) {
-        bool ignored = false;
-        latchBuffer(ignored, systemTime());
-    }
-    releasePendingBuffer(systemTime());
-}
-
-PixelFormat BufferStateLayer::getPixelFormat() const {
-    return mBufferInfo.mPixelFormat;
-}
-
-bool BufferStateLayer::getTransformToDisplayInverse() const {
-    return mBufferInfo.mTransformToDisplayInverse;
-}
-
-Rect BufferStateLayer::getBufferCrop() const {
-    // this is the crop rectangle that applies to the buffer
-    // itself (as opposed to the window)
-    if (!mBufferInfo.mCrop.isEmpty()) {
-        // if the buffer crop is defined, we use that
-        return mBufferInfo.mCrop;
-    } else if (mBufferInfo.mBuffer != nullptr) {
-        // otherwise we use the whole buffer
-        return mBufferInfo.mBuffer->getBounds();
-    } else {
-        // if we don't have a buffer yet, we use an empty/invalid crop
-        return Rect();
-    }
-}
-
-uint32_t BufferStateLayer::getBufferTransform() const {
-    return mBufferInfo.mTransform;
-}
-
-ui::Dataspace BufferStateLayer::getDataSpace() const {
-    return mBufferInfo.mDataspace;
-}
-
-ui::Dataspace BufferStateLayer::translateDataspace(ui::Dataspace dataspace) {
-    ui::Dataspace updatedDataspace = dataspace;
-    // translate legacy dataspaces to modern dataspaces
-    switch (dataspace) {
-        case ui::Dataspace::SRGB:
-            updatedDataspace = ui::Dataspace::V0_SRGB;
-            break;
-        case ui::Dataspace::SRGB_LINEAR:
-            updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
-            break;
-        case ui::Dataspace::JFIF:
-            updatedDataspace = ui::Dataspace::V0_JFIF;
-            break;
-        case ui::Dataspace::BT601_625:
-            updatedDataspace = ui::Dataspace::V0_BT601_625;
-            break;
-        case ui::Dataspace::BT601_525:
-            updatedDataspace = ui::Dataspace::V0_BT601_525;
-            break;
-        case ui::Dataspace::BT709:
-            updatedDataspace = ui::Dataspace::V0_BT709;
-            break;
-        default:
-            break;
-    }
-
-    return updatedDataspace;
-}
-
-sp<GraphicBuffer> BufferStateLayer::getBuffer() const {
-    return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
-}
-
-void BufferStateLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
-    sp<GraphicBuffer> buffer = getBuffer();
-    if (!buffer) {
-        ALOGE("Buffer should not be null!");
-        return;
-    }
-    GLConsumer::computeTransformMatrix(outMatrix, buffer->getWidth(), buffer->getHeight(),
-                                       buffer->getPixelFormat(), mBufferInfo.mCrop,
-                                       mBufferInfo.mTransform, filteringEnabled);
-}
-
-void BufferStateLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
-    Layer::setInitialValuesForClone(clonedFrom);
-
-    sp<BufferStateLayer> bufferClonedFrom =
-            sp<BufferStateLayer>::fromExisting(static_cast<BufferStateLayer*>(clonedFrom.get()));
-    mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha;
-    mPotentialCursor = bufferClonedFrom->mPotentialCursor;
-    mProtectedByApp = bufferClonedFrom->mProtectedByApp;
-
-    updateCloneBufferInfo();
-}
-
-void BufferStateLayer::updateCloneBufferInfo() {
-    if (!isClone() || !isClonedFromAlive()) {
-        return;
-    }
-
-    sp<BufferStateLayer> clonedFrom = sp<BufferStateLayer>::fromExisting(
-            static_cast<BufferStateLayer*>(getClonedFrom().get()));
-    mBufferInfo = clonedFrom->mBufferInfo;
-    mSidebandStream = clonedFrom->mSidebandStream;
-    surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
-    mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
-    mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
-
-    // After buffer info is updated, the drawingState from the real layer needs to be copied into
-    // the cloned. This is because some properties of drawingState can change when latchBuffer is
-    // called. However, copying the drawingState would also overwrite the cloned layer's relatives
-    // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
-    // the cloned drawingState again.
-    wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
-    SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
-    wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
-    WindowInfo tmpInputInfo = mDrawingState.inputInfo;
-
-    cloneDrawingState(clonedFrom.get());
-
-    mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
-    mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
-    mDrawingState.zOrderRelatives = tmpZOrderRelatives;
-    mDrawingState.inputInfo = tmpInputInfo;
-}
-
-void BufferStateLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
-    mTransformHint = getFixedTransformHint();
-    if (mTransformHint == ui::Transform::ROT_INVALID) {
-        mTransformHint = displayTransformHint;
-    }
-}
-
-const std::shared_ptr<renderengine::ExternalTexture>& BufferStateLayer::getExternalTexture() const {
-    return mBufferInfo.mBuffer;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
deleted file mode 100644
index 8bad3d2..0000000
--- a/services/surfaceflinger/BufferStateLayer.h
+++ /dev/null
@@ -1,307 +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 <sys/types.h>
-#include <cstdint>
-#include <list>
-#include <stack>
-
-#include <android/gui/ISurfaceComposerClient.h>
-#include <gui/LayerState.h>
-#include <renderengine/Image.h>
-#include <renderengine/Mesh.h>
-#include <renderengine/RenderEngine.h>
-#include <renderengine/Texture.h>
-#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
-#include <ui/FrameStats.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/PixelFormat.h>
-#include <ui/Region.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
-#include "Client.h"
-#include "DisplayHardware/HWComposer.h"
-#include "FrameTimeline.h"
-#include "FrameTracker.h"
-#include "HwcSlotGenerator.h"
-#include "Layer.h"
-#include "LayerVector.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-
-class SlotGenerationTest;
-
-class BufferStateLayer : public Layer {
-public:
-    explicit BufferStateLayer(const LayerCreationArgs&);
-
-    ~BufferStateLayer() override;
-
-    // Implements Layer.
-    sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
-    compositionengine::LayerFECompositionState* editCompositionState() override;
-
-    // If we have received a new buffer this frame, we will pass its surface
-    // damage down to hardware composer. Otherwise, we must send a region with
-    // one empty rect.
-    void useSurfaceDamage() override;
-    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;
-
-    // isProtected - true if the layer may contain protected content in the
-    // GRALLOC_USAGE_PROTECTED sense.
-    bool isProtected() const override;
-
-    bool usesSourceCrop() const override { return true; }
-
-    bool isHdrY410() const override;
-
-    void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
-                           const std::shared_ptr<FenceTime>& presentFence,
-                           const CompositorTiming&) override;
-
-    // latchBuffer - called each time the screen is redrawn and returns whether
-    // the visible regions need to be recomputed (this is a fairly heavy
-    // operation, so this should be set only if needed). Typically this is used
-    // to figure out if the content or size of a surface has changed.
-    bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
-    bool hasReadyFrame() const override;
-
-    // Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
-    // This is used if the buffer is just latched and releases to free up the buffer
-    // and will not be shown on screen.
-    // Should only be called on the main thread.
-    void latchAndReleaseBuffer() override;
-
-    bool getTransformToDisplayInverse() const override;
-
-    Rect getBufferCrop() const override;
-
-    uint32_t getBufferTransform() const override;
-
-    ui::Dataspace getDataSpace() const override;
-
-    sp<GraphicBuffer> getBuffer() const override;
-    const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const override;
-
-    ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
-
-    // Implements Layer.
-    const char* getType() const override { return "BufferStateLayer"; }
-
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
-
-    void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
-
-    Region getActiveTransparentRegion(const Layer::State& s) const override {
-        return s.transparentRegionHint;
-    }
-    Rect getCrop(const Layer::State& s) const;
-
-    bool setTransform(uint32_t transform) override;
-    bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
-    bool setCrop(const Rect& crop) override;
-    bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
-                   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;
-    bool setApi(int32_t api) override;
-    bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
-    bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
-    bool setPosition(float /*x*/, float /*y*/) override;
-    bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/);
-
-    // Override to ignore legacy layer state properties that are not used by BufferStateLayer
-    bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
-    bool setTransparentRegionHint(const Region& transparent) override;
-
-    // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
-    // and its parent layer is not bounded
-    Rect getBufferSize(const State& s) const override;
-    FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
-    void setAutoRefresh(bool autoRefresh) override;
-
-    bool setBufferCrop(const Rect& bufferCrop) override;
-    bool setDestinationFrame(const Rect& destinationFrame) override;
-    bool updateGeometry() override;
-
-    bool fenceHasSignaled() const;
-    bool onPreComposition(nsecs_t) override;
-
-    // See mPendingBufferTransactions
-    void decrementPendingBufferCount();
-    std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
-    std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
-
-protected:
-    void gatherBufferInfo();
-    void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
-    ui::Transform getInputTransform() const override;
-    Rect getInputBounds() const override;
-
-    struct BufferInfo {
-        nsecs_t mDesiredPresentTime;
-        std::shared_ptr<FenceTime> mFenceTime;
-        sp<Fence> mFence;
-        uint32_t mTransform{0};
-        ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
-        Rect mCrop;
-        uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
-        Region mSurfaceDamage;
-        HdrMetadata mHdrMetadata;
-        int mApi;
-        PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
-        bool mTransformToDisplayInverse{false};
-
-        std::shared_ptr<renderengine::ExternalTexture> mBuffer;
-        uint64_t mFrameNumber;
-        int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
-
-        bool mFrameLatencyNeeded{false};
-    };
-
-    BufferInfo mBufferInfo;
-
-    std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
-            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
-
-    /*
-     * compositionengine::LayerFE overrides
-     */
-    const compositionengine::LayerFECompositionState* getCompositionState() const override;
-    void preparePerFrameCompositionState() override;
-
-    static bool getOpacityForFormat(PixelFormat format);
-
-    // from graphics API
-    const uint32_t mTextureName;
-    ui::Dataspace translateDataspace(ui::Dataspace dataspace);
-    void setInitialValuesForClone(const sp<Layer>& clonedFrom);
-    void updateCloneBufferInfo() override;
-    uint64_t mPreviousFrameNumber = 0;
-
-    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 mDrawingState.autoRefresh; }
-    bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
-
-    std::atomic<bool> mSidebandStreamChanged{false};
-
-private:
-    friend class SlotGenerationTest;
-    friend class TransactionFrameTracerTest;
-    friend class TransactionSurfaceFrameTest;
-
-    // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
-    // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
-    // detection.
-    bool needsInputInfo() const override { return !mPotentialCursor; }
-
-    // Returns true if this layer requires filtering
-    bool needsFiltering(const DisplayDevice*) const override;
-    bool needsFilteringForScreenshots(const DisplayDevice*,
-                                      const ui::Transform& inverseParentTransform) const override;
-
-    PixelFormat getPixelFormat() const;
-
-    // Computes the transform matrix using the setFilteringEnabled to determine whether the
-    // transform matrix should be computed for use with bilinear filtering.
-    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
-
-    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
-
-    inline void tracePendingBufferCount(int32_t pendingBuffers);
-
-    bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
-                                 nsecs_t requestedPresentTime);
-
-    // Latch sideband stream and returns true if the dirty region should be updated.
-    bool latchSidebandStream(bool& recomputeVisibleRegions);
-
-    bool hasFrameUpdate() const;
-
-    void updateTexImage(nsecs_t latchTime);
-
-    sp<Layer> createClone() override;
-
-    // Crop that applies to the buffer
-    Rect computeBufferCrop(const State& s);
-
-    bool willPresentCurrentTransaction() const;
-
-    // Returns true if the transformed buffer size does not match the layer size and we need
-    // to apply filtering.
-    bool bufferNeedsFiltering() const;
-
-    bool simpleBufferUpdate(const layer_state_t& s) const override;
-
-    void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
-                                   const sp<GraphicBuffer>& buffer, uint64_t framenumber,
-                                   const sp<Fence>& releaseFence,
-                                   uint32_t currentMaxAcquiredBufferCount);
-
-    ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
-    uint64_t mPreviousReleasedFrameNumber = 0;
-
-    uint64_t mPreviousBarrierFrameNumber = 0;
-
-    bool mReleasePreviousBuffer = false;
-
-    // Stores the last set acquire fence signal time used to populate the callback handle's acquire
-    // time.
-    std::variant<nsecs_t, sp<Fence>> mCallbackHandleAcquireTimeOrFence = -1;
-
-    std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
-    // An upper bound on the number of SurfaceFrames in the pending classifications deque.
-    static constexpr int kPendingClassificationMaxSurfaceFrames = 25;
-
-    const std::string mBlastTransactionName{"BufferTX - " + mName};
-    // This integer is incremented everytime a buffer arrives at the server for this layer,
-    // and decremented when a buffer is dropped or latched. When changed the integer is exported
-    // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is
-    // possible to see when a buffer arrived at the server, and in which frame it latched.
-    //
-    // You can understand the trace this way:
-    //     - If the integer increases, a buffer arrived at the server.
-    //     - If the integer decreases in latchBuffer, that buffer was latched
-    //     - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
-    std::atomic<int32_t> mPendingBufferTransactions{0};
-
-    // Contains requested position and matrix updates. This will be applied if the client does
-    // not specify a destination frame.
-    ui::Transform mRequestedTransform;
-
-    sp<HwcSlotGenerator> mHwcSlotGenerator;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 3685bb4..30b8759 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -81,17 +81,9 @@
                                      gui::CreateSurfaceResult* outResult) {
     // We rely on createLayer to check permissions.
     sp<IBinder> handle;
-    int32_t layerId;
-    uint32_t transformHint;
     LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(),
                            static_cast<uint32_t>(flags), std::move(metadata));
-    const status_t status =
-            mFlinger->createLayer(args, &handle, parent, &layerId, nullptr, &transformHint);
-    if (status == NO_ERROR) {
-        outResult->handle = handle;
-        outResult->layerId = layerId;
-        outResult->transformHint = static_cast<int32_t>(transformHint);
-    }
+    const status_t status = mFlinger->createLayer(args, parent, *outResult);
     return binderStatusFromStatusT(status);
 }
 
@@ -134,31 +126,21 @@
 }
 
 binder::Status Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle,
-                                     gui::MirrorSurfaceResult* outResult) {
+                                     gui::CreateSurfaceResult* outResult) {
     sp<IBinder> handle;
-    int32_t layerId;
     LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), "MirrorRoot",
                            0 /* flags */, gui::LayerMetadata());
-    status_t status = mFlinger->mirrorLayer(args, mirrorFromHandle, &handle, &layerId);
-    if (status == NO_ERROR) {
-        outResult->handle = handle;
-        outResult->layerId = layerId;
-    }
+    status_t status = mFlinger->mirrorLayer(args, mirrorFromHandle, *outResult);
     return binderStatusFromStatusT(status);
 }
 
-binder::Status Client::mirrorDisplay(int64_t displayId, gui::MirrorSurfaceResult* outResult) {
+binder::Status Client::mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) {
     sp<IBinder> handle;
-    int32_t layerId;
     LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this),
                            "MirrorRoot-" + std::to_string(displayId), 0 /* flags */,
                            gui::LayerMetadata());
     std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
-    status_t status = mFlinger->mirrorDisplay(*id, args, &handle, &layerId);
-    if (status == NO_ERROR) {
-        outResult->handle = handle;
-        outResult->layerId = layerId;
-    }
+    status_t status = mFlinger->mirrorDisplay(*id, args, *outResult);
     return binderStatusFromStatusT(status);
 }
 
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 4e59dfd..02079a3 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -57,9 +57,9 @@
                                       gui::FrameStats* outStats) override;
 
     binder::Status mirrorSurface(const sp<IBinder>& mirrorFromHandle,
-                                 gui::MirrorSurfaceResult* outResult) override;
+                                 gui::CreateSurfaceResult* outResult) override;
 
-    binder::Status mirrorDisplay(int64_t displayId, gui::MirrorSurfaceResult* outResult) override;
+    binder::Status mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) override;
 
     // constant
     sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index cf932a8..b01932e 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -22,6 +22,7 @@
 #include <cinttypes>
 
 #include <android-base/stringprintf.h>
+#include <gui/TraceUtils.h>
 #include <renderengine/impl/ExternalTexture.h>
 
 #include "ClientCache.h"
@@ -36,12 +37,12 @@
                             ClientCacheBuffer** outClientCacheBuffer) {
     auto& [processToken, id] = cacheId;
     if (processToken == nullptr) {
-        ALOGE("failed to get buffer, invalid (nullptr) process token");
+        ALOGE_AND_TRACE("ClientCache::getBuffer - invalid (nullptr) process token");
         return false;
     }
     auto it = mBuffers.find(processToken);
     if (it == mBuffers.end()) {
-        ALOGE("failed to get buffer, invalid process token");
+        ALOGE_AND_TRACE("ClientCache::getBuffer - invalid process token");
         return false;
     }
 
@@ -49,7 +50,7 @@
 
     auto bufItr = processBuffers.find(id);
     if (bufItr == processBuffers.end()) {
-        ALOGV("failed to get buffer, invalid buffer id");
+        ALOGE_AND_TRACE("ClientCache::getBuffer - invalid buffer id");
         return false;
     }
 
@@ -61,12 +62,12 @@
 bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
     auto& [processToken, id] = cacheId;
     if (processToken == nullptr) {
-        ALOGE("failed to cache buffer: invalid process token");
+        ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) process token");
         return false;
     }
 
     if (!buffer) {
-        ALOGE("failed to cache buffer: invalid buffer");
+        ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) buffer");
         return false;
     }
 
@@ -79,7 +80,7 @@
     if (it == mBuffers.end()) {
         token = processToken.promote();
         if (!token) {
-            ALOGE("failed to cache buffer: invalid token");
+            ALOGE_AND_TRACE("ClientCache::add - invalid token");
             return false;
         }
 
@@ -87,7 +88,7 @@
         if (token->localBinder() == nullptr) {
             status_t err = token->linkToDeath(mDeathRecipient);
             if (err != NO_ERROR) {
-                ALOGE("failed to cache buffer: could not link to death");
+                ALOGE_AND_TRACE("ClientCache::add - could not link to death");
                 return false;
             }
         }
@@ -102,7 +103,7 @@
     auto& processBuffers = it->second.second;
 
     if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
-        ALOGE("failed to cache buffer: cache is full");
+        ALOGE_AND_TRACE("ClientCache::add - cache is full");
         return false;
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 11a9e19..0ae8bf9 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -9,7 +9,10 @@
 
 cc_defaults {
     name: "libcompositionengine_defaults",
-    defaults: ["surfaceflinger_defaults"],
+    defaults: [
+        "android.hardware.graphics.composer3-ndk_shared",
+        "surfaceflinger_defaults",
+    ],
     cflags: [
         "-DLOG_TAG=\"CompositionEngine\"",
         "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
@@ -20,7 +23,6 @@
         "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-V2-cpp",
@@ -40,7 +42,6 @@
         "libmath",
         "librenderengine",
         "libtonemap",
-        "libtrace_proto",
         "libaidlcommonsupport",
         "libprocessgroup",
         "libcgrouprc",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 3faa068..6832ae1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -18,9 +18,10 @@
 
 #include <TimeStats/TimeStats.h>
 #include <utils/Timers.h>
-
 #include <memory>
 
+#include "Feature.h"
+
 namespace android {
 
 class HWComposer;
@@ -72,6 +73,8 @@
     // TODO(b/121291683): These will become private/internal
     virtual void preComposition(CompositionRefreshArgs&) = 0;
 
+    virtual FeatureFlags getFeatureFlags() const = 0;
+
     // Debugging
     virtual void dump(std::string&) const = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 16cb41b..5e84be1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -56,6 +56,9 @@
     // similar requests if needed.
     virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
 
+    // Sends the brightness setting to HWC
+    virtual void applyDisplayBrightness(const bool applyImmediately) = 0;
+
 protected:
     ~Display() = default;
 };
diff --git a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
similarity index 66%
rename from services/surfaceflinger/CompositionEngine/tests/TestUtils.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
index c80fde6..ee8000a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -13,19 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-#include <future>
+#include <ftl/flags.h>
+#include <cstdint>
 
 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
+enum class Feature : int32_t {
+    kSnapshotLayerMetadata = 1 << 0,
+};
+
+using FeatureFlags = ftl::Flags<Feature>;
+
+} // namespace android::compositionengine
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/FenceResult.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/FenceResult.h
deleted file mode 100644
index 0ce263b..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/FenceResult.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android-base/expected.h>
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-
-// TODO(b/232535621): Pull this file to <ui/FenceResult.h> so that RenderEngine::drawLayers returns
-// FenceResult rather than RenderEngineResult.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <renderengine/RenderEngine.h>
-#pragma clang diagnostic pop
-
-namespace android {
-
-class Fence;
-
-using FenceResult = base::expected<sp<Fence>, status_t>;
-
-// TODO(b/232535621): Prevent base::unexpected(NO_ERROR) from being a valid FenceResult.
-inline status_t fenceStatus(const FenceResult& fenceResult) {
-    return fenceResult.ok() ? NO_ERROR : fenceResult.error();
-}
-
-inline FenceResult toFenceResult(renderengine::RenderEngineResult&& result) {
-    if (auto [status, fence] = std::move(result); fence.ok()) {
-        return sp<Fence>::make(std::move(fence));
-    } else {
-        return base::unexpected(status);
-    }
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index ec610c1..fe8cad5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -20,8 +20,6 @@
 #include <ostream>
 #include <unordered_set>
 
-#include <compositionengine/FenceResult.h>
-
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
@@ -33,6 +31,7 @@
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <ftl/future.h>
+#include <ui/FenceResult.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
@@ -40,6 +39,10 @@
 
 class Fence;
 
+namespace gui {
+struct LayerMetadata;
+}
+
 namespace compositionengine {
 
 struct LayerFECompositionState;
@@ -54,31 +57,8 @@
     // Called before composition starts. Should return true if this layer has
     // pending updates which would require an extra display refresh cycle to
     // process.
-    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
-
-    // Used with latchCompositionState()
-    enum class StateSubset {
-        // Gets the basic geometry (bounds, transparent region, visibility,
-        // transforms, alpha) for the layer, for computing visibility and
-        // coverage.
-        BasicGeometry,
-
-        // Gets the full geometry (crops, buffer transforms, metadata) and
-        // content (buffer or color) state for the layer.
-        GeometryAndContent,
-
-        // Gets the per frame content (buffer or color) state for the layer.
-        Content,
-
-        // Gets the cursor state for the layer.
-        Cursor,
-    };
-
-    // Prepares the output-independent composition state for the layer. The
-    // StateSubset argument selects what portion of the state is actually needed
-    // by the CompositionEngine code, since computing everything may be
-    // expensive.
-    virtual void prepareCompositionState(StateSubset) = 0;
+    virtual bool onPreComposition(nsecs_t refreshStartTime,
+                                  bool updatingOutputGeometryThisFrame) = 0;
 
     struct ClientCompositionTargetSettings {
         enum class BlurSetting {
@@ -150,11 +130,11 @@
         uint64_t frameNumber = 0;
     };
 
-    // Returns the z-ordered list of LayerSettings to pass to RenderEngine::drawLayers. The list
-    // may contain shadows casted by the layer or the content of the layer itself.  If the layer
-    // does not render then an empty list will be returned.
-    virtual std::vector<LayerSettings> prepareClientCompositionList(
-            ClientCompositionTargetSettings&) = 0;
+    // Returns the LayerSettings to pass to RenderEngine::drawLayers. The state may contain shadows
+    // casted by the layer or the content of the layer itself. If the layer does not render then an
+    // empty optional will be returned.
+    virtual std::optional<LayerSettings> prepareClientComposition(
+            ClientCompositionTargetSettings&) const = 0;
 
     // Called after the layer is displayed to update the presentation fence
     virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>) = 0;
@@ -168,6 +148,8 @@
     // Whether the layer should be rendered with rounded corners.
     virtual bool hasRoundedCorners() const = 0;
     virtual void setWasClientComposed(const sp<Fence>&) {}
+    virtual const gui::LayerMetadata* getMetadata() const = 0;
+    virtual const gui::LayerMetadata* getRelativeMetadata() const = 0;
 };
 
 // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 2203639..874b330 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -262,9 +262,6 @@
     // Presents the output, finalizing all composition details
     virtual void present(const CompositionRefreshArgs&) = 0;
 
-    // Latches the front-end layer state for each output layer
-    virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
-
     // Enables predicting composition strategy to run client composition earlier
     virtual void setPredictCompositionStrategy(bool) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index bf5184e..6d0c395 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -126,9 +126,9 @@
     // Returns true if the composition settings scale pixels
     virtual bool needsFiltering() const = 0;
 
-    // Returns a composition list to be used by RenderEngine if the layer has been overridden
+    // Returns LayerSettings to be used by RenderEngine if the layer has been overridden
     // during the composition process
-    virtual std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const = 0;
+    virtual std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const = 0;
 
     // Debugging
     virtual void dump(std::string& result) const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 386808d..dd4dbe9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -48,11 +48,11 @@
 
     void preComposition(CompositionRefreshArgs&) override;
 
+    FeatureFlags getFeatureFlags() const override;
+
     // Debugging
     void dump(std::string&) const override;
 
-    void updateLayerStateFromFE(CompositionRefreshArgs& args);
-
     // Testing
     void setNeedsAnotherUpdateForTest(bool);
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index fa7bc5d..33a10a3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -72,6 +72,7 @@
             const compositionengine::DisplayColorProfileCreationArgs&) override;
     void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
     void createClientCompositionCache(uint32_t cacheSize) override;
+    void applyDisplayBrightness(const bool applyImmediately) override;
 
     // Internal helpers used by chooseCompositionStrategy()
     using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index fc8dd8b..23d5570 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -89,7 +89,6 @@
                                     compositionengine::Output::CoverageState&) override;
     void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
 
-    void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
     void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void planComposition() override;
     void writeCompositionState(const compositionengine::CompositionRefreshArgs&) override;
@@ -152,6 +151,8 @@
     virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0;
     virtual void dumpState(std::string& out) const = 0;
 
+    bool mustRecompose() const;
+
 private:
     void dirtyEntireOutput();
     void updateCompositionStateForBorder(const compositionengine::CompositionRefreshArgs&);
@@ -171,6 +172,9 @@
     std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
     std::unique_ptr<planner::Planner> mPlanner;
     std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
+
+    // Whether the content must be recomposed this frame.
+    bool mMustRecompose = false;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index ecd432f..6d4abf9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -18,6 +18,7 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <string>
 
 #include <compositionengine/LayerFE.h>
@@ -57,7 +58,7 @@
     void prepareForDeviceLayerRequests() override;
     void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
     bool needsFiltering() const override;
-    std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
+    std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
 
     void dump(std::string&) const override;
     virtual FloatRect calculateOutputSourceCrop(uint32_t internalDisplayRotationFlags) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index e65aa73..24a7744 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -115,7 +115,7 @@
 
     // Renders the cached set with the supplied output composition state.
     void render(renderengine::RenderEngine& re, TexturePool& texturePool,
-                const OutputCompositionState& outputState);
+                const OutputCompositionState& outputState, bool deviceHandlesColorTransform);
 
     void dump(std::string& result) const;
 
@@ -150,8 +150,6 @@
     bool hasSolidColorLayers() const;
 
 private:
-    CachedSet() = default;
-
     const NonBufferHash mFingerprint;
     std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
     std::vector<Layer> mLayers;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 92cc484..f934cb2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -106,7 +106,8 @@
 
     // Renders the newest cached sets with the supplied output composition state
     void renderCachedSets(const OutputCompositionState& outputState,
-                          std::optional<std::chrono::steady_clock::time_point> renderDeadline);
+                          std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+                          bool deviceHandlesColorTransform);
 
     void setTexturePoolEnabled(bool enabled) { mTexturePool.setEnabled(enabled); }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index b7ebca6..c968df7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -65,7 +65,8 @@
     // Rendering a pending cached set is optional: if the renderDeadline is not far enough in the
     // future then the planner may opt to skip rendering the cached set.
     void renderCachedSets(const OutputCompositionState& outputState,
-                          std::optional<std::chrono::steady_clock::time_point> renderDeadline);
+                          std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+                          bool deviceHandlesColorTransform);
 
     void setTexturePoolEnabled(bool enabled) { mFlattener.setTexturePoolEnabled(enabled); }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index f953d0b..a48cc6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -53,6 +53,8 @@
 
     MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
 
+    MOCK_CONST_METHOD0(getFeatureFlags, FeatureFlags());
+
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 72e6f3b..7e99ec2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -41,6 +41,7 @@
     MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
     MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
     MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
+    MOCK_METHOD1(applyDisplayBrightness, void(const bool));
     MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 1c5c10f..14922a4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -42,18 +42,19 @@
 
     MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*());
 
-    MOCK_METHOD1(onPreComposition, bool(nsecs_t));
+    MOCK_METHOD2(onPreComposition, bool(nsecs_t, bool));
 
-    MOCK_METHOD1(prepareCompositionState, void(compositionengine::LayerFE::StateSubset));
-    MOCK_METHOD1(prepareClientCompositionList,
-                 std::vector<compositionengine::LayerFE::LayerSettings>(
-                         compositionengine::LayerFE::ClientCompositionTargetSettings&));
+    MOCK_CONST_METHOD1(prepareClientComposition,
+                       std::optional<compositionengine::LayerFE::LayerSettings>(
+                               compositionengine::LayerFE::ClientCompositionTargetSettings&));
 
     MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>), (override));
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
     MOCK_CONST_METHOD0(hasRoundedCorners, bool());
+    MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
+    MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 2a04949..7592cac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -91,7 +91,6 @@
                  void(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&));
     MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
 
-    MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
     MOCK_METHOD1(updateCompositionState, void(const CompositionRefreshArgs&));
     MOCK_METHOD0(planComposition, void());
     MOCK_METHOD1(writeCompositionState, void(const CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index a6cb811..c22f1bf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <optional>
+
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/Output.h>
@@ -51,7 +53,7 @@
     MOCK_METHOD0(prepareForDeviceLayerRequests, void());
     MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
     MOCK_CONST_METHOD0(needsFiltering, bool());
-    MOCK_CONST_METHOD0(getOverrideCompositionList, std::vector<LayerFE::LayerSettings>());
+    MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>());
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 6203dc6..a4e1fff 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -105,8 +105,6 @@
         }
     }
 
-    updateLayerStateFromFE(args);
-
     for (const auto& output : args.outputs) {
         output->present(args);
     }
@@ -119,8 +117,6 @@
     for (const auto& output : args.outputs) {
         for (auto* layer : output->getOutputLayersOrderedByZ()) {
             if (layer->isHardwareCursor()) {
-                // Latch the cursor composition state from each front-end layer.
-                layer->getLayerFE().prepareCompositionState(LayerFE::StateSubset::Cursor);
                 layer->writeCursorPositionToHWC();
             }
         }
@@ -136,7 +132,7 @@
     mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     for (auto& layer : args.layers) {
-        if (layer->onPreComposition(mRefreshStartTime)) {
+        if (layer->onPreComposition(mRefreshStartTime, args.updatingOutputGeometryThisFrame)) {
             needsAnotherUpdate = true;
         }
     }
@@ -144,6 +140,10 @@
     mNeedsAnotherUpdate = needsAnotherUpdate;
 }
 
+FeatureFlags CompositionEngine::getFeatureFlags() const {
+    return {};
+}
+
 void CompositionEngine::dump(std::string&) const {
     // The base class has no state to dump, but derived classes might.
 }
@@ -152,12 +152,5 @@
     mNeedsAnotherUpdate = value;
 }
 
-void CompositionEngine::updateLayerStateFromFE(CompositionRefreshArgs& args) {
-    // Update the composition state from each front-end layer
-    for (const auto& output : args.outputs) {
-        output->updateLayerStateFromFE(args);
-    }
-}
-
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 09adaed..0b69d44 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -203,6 +203,24 @@
     setReleasedLayers(std::move(releasedLayers));
 }
 
+void Display::applyDisplayBrightness(const bool applyImmediately) {
+    auto& hwc = getCompositionEngine().getHwComposer();
+    const auto halDisplayId = HalDisplayId::tryCast(*getDisplayId());
+    if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
+        physicalDisplayId && getState().displayBrightness) {
+        const status_t result =
+                hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
+                                         getState().displayBrightnessNits,
+                                         Hwc2::Composer::DisplayBrightnessOptions{
+                                                 .applyImmediately = applyImmediately})
+                        .get();
+        ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
+                 getName().c_str(), result, strerror(-result));
+    }
+    // Clear out the display brightness now that it's been communicated to composer.
+    editState().displayBrightness.reset();
+}
+
 void Display::beginFrame() {
     Output::beginFrame();
 
@@ -212,20 +230,7 @@
         return;
     }
 
-    auto& hwc = getCompositionEngine().getHwComposer();
-    if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
-        physicalDisplayId && getState().displayBrightness) {
-        const status_t result =
-                hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
-                                         getState().displayBrightnessNits,
-                                         Hwc2::Composer::DisplayBrightnessOptions{
-                                                 .applyImmediately = false})
-                        .get();
-        ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
-                 getName().c_str(), result, strerror(-result));
-    }
-    // Clear out the display brightness now that it's been communicated to composer.
-    editState().displayBrightness.reset();
+    applyDisplayBrightness(false);
 }
 
 bool Display::chooseCompositionStrategy(
@@ -243,7 +248,7 @@
         return false;
     }
 
-    const nsecs_t startTime = systemTime();
+    const TimePoint startTime = TimePoint::now();
 
     // Get any composition changes requested by the HWC device, and apply them.
     std::optional<android::HWComposer::DeviceRequestedChanges> changes;
@@ -261,7 +266,7 @@
     }
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcValidateTiming(mId, startTime, systemTime());
+        mPowerAdvisor->setHwcValidateTiming(mId, startTime, TimePoint::now());
         mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
     }
 
@@ -365,7 +370,7 @@
 
     auto& hwc = getCompositionEngine().getHwComposer();
 
-    const nsecs_t startTime = systemTime();
+    const TimePoint startTime = TimePoint::now();
 
     if (isPowerHintSessionEnabled()) {
         if (!getCompositionEngine().getHwComposer().getComposer()->isSupported(
@@ -379,7 +384,7 @@
                                    getState().previousPresentFence);
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcPresentTiming(mId, startTime, systemTime());
+        mPowerAdvisor->setHwcPresentTiming(mId, startTime, TimePoint::now());
     }
 
     fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
@@ -421,7 +426,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().isEmpty()) {
+    if (GpuVirtualDisplayId::tryCast(mId) && !mustRecompose()) {
         ALOGV("Skipping display composition");
         return;
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 8ebc5b1..e3f3680 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -501,7 +501,6 @@
     // appear on multiple outputs.
     if (!coverage.latchedLayers.count(layerFE)) {
         coverage.latchedLayers.insert(layerFE);
-        layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
     }
 
     // Only consider the layers on this output
@@ -725,14 +724,6 @@
     // The base class does nothing with this call.
 }
 
-void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
-    for (auto* layer : getOutputLayersOrderedByZ()) {
-        layer->getLayerFE().prepareCompositionState(
-                args.updatingGeometryThisFrame ? LayerFE::StateSubset::GeometryAndContent
-                                               : LayerFE::StateSubset::Content);
-    }
-}
-
 void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
@@ -1015,17 +1006,17 @@
     //   frame, then nothing more until we get new layers.
     // - When a display is created with a private layer stack, we won't
     //   emit any black frames until a layer is added to the layer stack.
-    const bool mustRecompose = dirty && !(empty && wasEmpty);
+    mMustRecompose = dirty && !(empty && wasEmpty);
 
     const char flagPrefix[] = {'-', '+'};
     static_cast<void>(flagPrefix);
-    ALOGV_IF("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __FUNCTION__,
-             mustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
-             flagPrefix[empty], flagPrefix[wasEmpty]);
+    ALOGV("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __func__,
+          mMustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
+          flagPrefix[empty], flagPrefix[wasEmpty]);
 
-    mRenderSurface->beginFrame(mustRecompose);
+    mRenderSurface->beginFrame(mMustRecompose);
 
-    if (mustRecompose) {
+    if (mMustRecompose) {
         outputState.lastCompositionHadVisibleLayers = !empty;
     }
 }
@@ -1330,11 +1321,10 @@
     // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
     const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay;
 
-    auto fenceResult =
-            toFenceResult(renderEngine
-                                  .drawLayers(clientCompositionDisplay, clientRenderEngineLayers,
-                                              tex, useFramebufferCache, std::move(fd))
-                                  .get());
+    auto fenceResult = renderEngine
+                               .drawLayers(clientCompositionDisplay, clientRenderEngineLayers, tex,
+                                           useFramebufferCache, std::move(fd))
+                               .get();
 
     if (mClientCompositionRequestCache && fenceStatus(fenceResult) != NO_ERROR) {
         // If rendering was not successful, remove the request from the cache.
@@ -1366,7 +1356,7 @@
     bool firstLayer = true;
 
     bool disableBlurs = false;
-    sp<GraphicBuffer> previousOverrideBuffer = nullptr;
+    uint64_t previousOverrideBufferId = 0;
 
     for (auto* layer : getOutputLayersOrderedByZ()) {
         const auto& layerState = layer->getState();
@@ -1402,11 +1392,10 @@
                 !layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
 
         if (clientComposition || clearClientComposition) {
-            std::vector<LayerFE::LayerSettings> results;
-            if (layer->getState().overrideInfo.buffer != nullptr) {
-                if (layer->getState().overrideInfo.buffer->getBuffer() != previousOverrideBuffer) {
-                    results = layer->getOverrideCompositionList();
-                    previousOverrideBuffer = layer->getState().overrideInfo.buffer->getBuffer();
+            if (auto overrideSettings = layer->getOverrideCompositionSettings()) {
+                if (overrideSettings->bufferId != previousOverrideBufferId) {
+                    previousOverrideBufferId = overrideSettings->bufferId;
+                    clientCompositionLayers.push_back(std::move(*overrideSettings));
                     ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
                 } else {
                     ALOGV("Skipping redundant override buffer for [%s] in RE",
@@ -1432,20 +1421,18 @@
                                        .clearContent = !clientComposition,
                                        .blurSetting = blurSetting,
                                        .whitePointNits = layerState.whitePointNits};
-                results = layerFE.prepareClientCompositionList(targetSettings);
-                if (realContentIsVisible && !results.empty()) {
-                    layer->editState().clientCompositionTimestamp = systemTime();
+                if (auto clientCompositionSettings =
+                            layerFE.prepareClientComposition(targetSettings)) {
+                    clientCompositionLayers.push_back(std::move(*clientCompositionSettings));
+                    if (realContentIsVisible) {
+                        layer->editState().clientCompositionTimestamp = systemTime();
+                    }
                 }
             }
 
             if (clientComposition) {
                 outLayerFEs.push_back(&layerFE);
             }
-
-            clientCompositionLayers.insert(clientCompositionLayers.end(),
-                                           std::make_move_iterator(results.begin()),
-                                           std::make_move_iterator(results.end()));
-            results.clear();
         }
 
         firstLayer = false;
@@ -1539,7 +1526,8 @@
 
 void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
     if (mPlanner) {
-        mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime);
+        mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime,
+                                   getState().usesDeviceComposition || getSkipColorTransform());
     }
 }
 
@@ -1634,5 +1622,9 @@
     mRenderSurface->prepareFrame(state.usesClientComposition, state.usesDeviceComposition);
 }
 
+bool Output::mustRecompose() const {
+    return mMustRecompose;
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 1bb9d0eb..a39c527 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -787,7 +787,7 @@
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
-std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() const {
+std::optional<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionSettings() const {
     if (getState().overrideInfo.buffer == nullptr) {
         return {};
     }
@@ -816,7 +816,7 @@
     settings.alpha = 1.0f;
     settings.whitePointNits = getOutput().getState().sdrWhitePointNits;
 
-    return {static_cast<LayerFE::LayerSettings>(settings)};
+    return settings;
 }
 
 void OutputLayer::dump(std::string& out) const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 641b806..0731d48 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -159,7 +159,8 @@
 }
 
 void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
-                       const OutputCompositionState& outputState) {
+                       const OutputCompositionState& outputState,
+                       bool deviceHandlesColorTransform) {
     ATRACE_CALL();
     const Rect& viewport = outputState.layerStackSpace.getContent();
     const ui::Dataspace& outputDataspace = outputState.dataspace;
@@ -170,6 +171,8 @@
             .physicalDisplay = outputState.framebufferSpace.getContent(),
             .clip = viewport,
             .outputDataspace = outputDataspace,
+            .colorTransform = outputState.colorTransformMatrix,
+            .deviceHandlesColorTransform = deviceHandlesColorTransform,
             .orientation = orientation,
             .targetLuminanceNits = outputState.displayBrightnessNits,
     };
@@ -190,11 +193,11 @@
     std::vector<renderengine::LayerSettings> layerSettings;
     renderengine::LayerSettings highlight;
     for (const auto& layer : mLayers) {
-        const auto clientCompositionList =
-                layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
-                        targetSettings);
-        layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(),
-                             clientCompositionList.cend());
+        if (auto clientCompositionSettings =
+                    layer.getState()->getOutputLayer()->getLayerFE().prepareClientComposition(
+                            targetSettings)) {
+            layerSettings.push_back(std::move(*clientCompositionSettings));
+        }
     }
 
     renderengine::LayerSettings blurLayerSettings;
@@ -202,43 +205,40 @@
         auto blurSettings = targetSettings;
         blurSettings.blurSetting =
                 LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly;
-        auto clientCompositionList =
-                mBlurLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
-                        blurSettings);
-        blurLayerSettings = clientCompositionList.back();
+
+        auto blurLayerSettings =
+                mBlurLayer->getOutputLayer()->getLayerFE().prepareClientComposition(blurSettings);
         // This mimics Layer::prepareClearClientComposition
-        blurLayerSettings.skipContentDraw = true;
-        blurLayerSettings.name = std::string("blur layer");
+        blurLayerSettings->skipContentDraw = true;
+        blurLayerSettings->name = std::string("blur layer");
         // Clear out the shadow settings
-        blurLayerSettings.shadow = {};
-        layerSettings.push_back(blurLayerSettings);
+        blurLayerSettings->shadow = {};
+        layerSettings.push_back(std::move(*blurLayerSettings));
     }
 
-    renderengine::LayerSettings holePunchSettings;
-    renderengine::LayerSettings holePunchBackgroundSettings;
     if (mHolePunchLayer) {
         auto& layerFE = mHolePunchLayer->getOutputLayer()->getLayerFE();
-        auto clientCompositionList = layerFE.prepareClientCompositionList(targetSettings);
-        // Assume that the final layer contains the buffer that we want to
-        // replace with a hole punch.
-        holePunchSettings = clientCompositionList.back();
+
+        auto holePunchSettings = layerFE.prepareClientComposition(targetSettings);
         // This mimics Layer::prepareClearClientComposition
-        holePunchSettings.source.buffer.buffer = nullptr;
-        holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
-        holePunchSettings.disableBlending = true;
-        holePunchSettings.alpha = 0.0f;
-        holePunchSettings.name =
+        holePunchSettings->source.buffer.buffer = nullptr;
+        holePunchSettings->source.solidColor = half3(0.0f, 0.0f, 0.0f);
+        holePunchSettings->disableBlending = true;
+        holePunchSettings->alpha = 0.0f;
+        holePunchSettings->name =
                 android::base::StringPrintf("hole punch layer for %s", layerFE.getDebugName());
-        layerSettings.push_back(holePunchSettings);
 
         // Add a solid background as the first layer in case there is no opaque
         // buffer behind the punch hole
+        renderengine::LayerSettings holePunchBackgroundSettings;
         holePunchBackgroundSettings.alpha = 1.0f;
         holePunchBackgroundSettings.name = std::string("holePunchBackground");
-        holePunchBackgroundSettings.geometry.boundaries = holePunchSettings.geometry.boundaries;
+        holePunchBackgroundSettings.geometry.boundaries = holePunchSettings->geometry.boundaries;
         holePunchBackgroundSettings.geometry.positionTransform =
-                holePunchSettings.geometry.positionTransform;
-        layerSettings.emplace(layerSettings.begin(), holePunchBackgroundSettings);
+                holePunchSettings->geometry.positionTransform;
+        layerSettings.emplace(layerSettings.begin(), std::move(holePunchBackgroundSettings));
+
+        layerSettings.push_back(std::move(*holePunchSettings));
     }
 
     if (sDebugHighlighLayers) {
@@ -273,11 +273,10 @@
 
     constexpr bool kUseFramebufferCache = false;
 
-    auto fenceResult =
-            toFenceResult(renderEngine
-                                  .drawLayers(displaySettings, layerSettings, texture->get(),
-                                              kUseFramebufferCache, std::move(bufferFence))
-                                  .get());
+    auto fenceResult = renderEngine
+                               .drawLayers(displaySettings, layerSettings, texture->get(),
+                                           kUseFramebufferCache, std::move(bufferFence))
+                               .get();
 
     if (fenceStatus(fenceResult) == NO_ERROR) {
         mDrawFence = std::move(fenceResult).value_or(Fence::NO_FENCE);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 1062b70..9175dd0 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -99,7 +99,8 @@
 
 void Flattener::renderCachedSets(
         const OutputCompositionState& outputState,
-        std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
+        std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+        bool deviceHandlesColorTransform) {
     ATRACE_CALL();
 
     if (!mNewCachedSet) {
@@ -136,7 +137,7 @@
         }
     }
 
-    mNewCachedSet->render(mRenderEngine, mTexturePool, outputState);
+    mNewCachedSet->render(mRenderEngine, mTexturePool, outputState, deviceHandlesColorTransform);
 }
 
 void Flattener::dumpLayers(std::string& result) const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index c8413eb..54133d9 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -201,11 +201,11 @@
                             finalPlan);
 }
 
-void Planner::renderCachedSets(
-        const OutputCompositionState& outputState,
-        std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
+void Planner::renderCachedSets(const OutputCompositionState& outputState,
+                               std::optional<std::chrono::steady_clock::time_point> renderDeadline,
+                               bool deviceHandlesColorTransform) {
     ATRACE_CALL();
-    mFlattener.renderCachedSets(outputState, renderDeadline);
+    mFlattener.renderCachedSets(outputState, renderDeadline, deviceHandlesColorTransform);
 }
 
 void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index de9de01..b570979 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -108,12 +108,6 @@
     EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
     EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
 
-    // The next step in presenting is to make sure all outputs have the latest
-    // state from the front-end (SurfaceFlinger).
-    EXPECT_CALL(*mOutput1, updateLayerStateFromFE(Ref(mRefreshArgs)));
-    EXPECT_CALL(*mOutput2, updateLayerStateFromFE(Ref(mRefreshArgs)));
-    EXPECT_CALL(*mOutput3, updateLayerStateFromFE(Ref(mRefreshArgs)));
-
     // The last step is to actually present each output.
     EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
     EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
@@ -175,21 +169,18 @@
     {
         InSequence seq;
         EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC());
     }
 
     {
         InSequence seq;
         EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC());
     }
 
     {
         InSequence seq;
         EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC());
     }
 
@@ -222,9 +213,12 @@
     nsecs_t ts1 = 0;
     nsecs_t ts2 = 0;
     nsecs_t ts3 = 0;
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
 
     mRefreshArgs.outputs = {mOutput1};
     mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
@@ -238,9 +232,9 @@
 }
 
 TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
 
     mEngine.setNeedsAnotherUpdateForTest(true);
 
@@ -255,9 +249,9 @@
 
 TEST_F(CompositionTestPreComposition,
        preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
 
     mRefreshArgs.outputs = {mOutput1};
     mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 51ca213..95459c0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -971,16 +971,40 @@
 
     // We expect no calls to queueBuffer if composition was skipped.
     EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+    EXPECT_CALL(*renderSurface, beginFrame(false));
 
     gpuDisplay->editState().isEnabled = true;
     gpuDisplay->editState().usesClientComposition = false;
     gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+    gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
 
+    gpuDisplay->beginFrame();
     gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
 }
 
-TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
+TEST_F(DisplayFinishFrameTest, skipsCompositionIfEmpty) {
+    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 no calls to queueBuffer if composition was skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+    EXPECT_CALL(*renderSurface, beginFrame(false));
+
+    gpuDisplay->editState().isEnabled = true;
+    gpuDisplay->editState().usesClientComposition = false;
+    gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
+    gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+    gpuDisplay->editState().lastCompositionHadVisibleLayers = false;
+
+    gpuDisplay->beginFrame();
+    gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+}
+
+TEST_F(DisplayFinishFrameTest, performsCompositionIfDirtyAndNotEmpty) {
     auto args = getDisplayCreationArgsForGpuVirtualDisplay();
     std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
 
@@ -989,11 +1013,15 @@
 
     // We expect a single call to queueBuffer when composition is not skipped.
     EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+    EXPECT_CALL(*renderSurface, beginFrame(true));
 
     gpuDisplay->editState().isEnabled = true;
     gpuDisplay->editState().usesClientComposition = false;
     gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+    gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
+
+    gpuDisplay->beginFrame();
     gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index d7704a8..9102139 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -70,6 +70,7 @@
     MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
     MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
     MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
+    MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
     MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
     MOCK_METHOD3(setOutputBuffer,
                  status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index c8bd5e4..e220541 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -38,7 +38,7 @@
     MOCK_METHOD(bool, usePowerHintSession, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
-    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (Duration targetDuration), (override));
     MOCK_METHOD(void, sendActualWorkDuration, (), (override));
     MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
     MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
@@ -46,25 +46,24 @@
     MOCK_METHOD(void, setGpuFenceTime,
                 (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
     MOCK_METHOD(void, setHwcValidateTiming,
-                (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+                (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime),
                 (override));
     MOCK_METHOD(void, setHwcPresentTiming,
-                (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+                (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
                 (override));
     MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
     MOCK_METHOD(void, setRequiresClientComposition,
                 (DisplayId displayId, bool requiresClientComposition), (override));
-    MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
-    MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+    MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
+    MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
                 (override));
     MOCK_METHOD(void, setHwcPresentDelayedTime,
-                (DisplayId displayId,
-                 std::chrono::steady_clock::time_point earliestFrameStartTime));
-    MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
-    MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
-    MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+                (DisplayId displayId, TimePoint earliestFrameStartTime));
+    MOCK_METHOD(void, setFrameDelay, (Duration frameDelayDuration), (override));
+    MOCK_METHOD(void, setCommitStart, (TimePoint commitStartTime), (override));
+    MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override));
     MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
-    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override));
+    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ad0fb9d..eb209e9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -39,7 +39,6 @@
 #include "CallOrderStateMachineHelper.h"
 #include "MockHWC2.h"
 #include "RegionMatcher.h"
-#include "TestUtils.h"
 
 namespace android::compositionengine {
 namespace {
@@ -759,56 +758,6 @@
 }
 
 /*
- * Output::updateLayerStateFromFE()
- */
-
-using OutputUpdateLayerStateFromFETest = OutputTest;
-
-TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) {
-    CompositionRefreshArgs refreshArgs;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-TEST_F(OutputUpdateLayerStateFromFETest, preparesContentStateForAllContainedLayers) {
-    InjectedLayer layer1;
-    InjectedLayer layer2;
-    InjectedLayer layer3;
-
-    EXPECT_CALL(*layer1.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-    EXPECT_CALL(*layer2.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-    EXPECT_CALL(*layer3.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-
-    injectOutputLayer(layer1);
-    injectOutputLayer(layer2);
-    injectOutputLayer(layer3);
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.updatingGeometryThisFrame = false;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-TEST_F(OutputUpdateLayerStateFromFETest, preparesGeometryAndContentStateForAllContainedLayers) {
-    InjectedLayer layer1;
-    InjectedLayer layer2;
-    InjectedLayer layer3;
-
-    EXPECT_CALL(*layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-    EXPECT_CALL(*layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-    EXPECT_CALL(*layer3.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-
-    injectOutputLayer(layer1);
-    injectOutputLayer(layer2);
-    injectOutputLayer(layer3);
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.updatingGeometryThisFrame = true;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-/*
  * Output::updateAndWriteCompositionState()
  */
 
@@ -1537,9 +1486,6 @@
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
     EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer.layerFE,
-                prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
-
     mGeomSnapshots.clear();
 
     ensureOutputLayerIfVisible();
@@ -3527,9 +3473,8 @@
             .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()});
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
+                return ftl::yield<FenceResult>(Fence::NO_FENCE);
             });
     verify().execute().expectAFenceWasReturned();
 }
@@ -3559,9 +3504,8 @@
             .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()});
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
+                return ftl::yield<FenceResult>(Fence::NO_FENCE);
             });
 
     verify().execute().expectAFenceWasReturned();
@@ -3594,9 +3538,8 @@
             .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()});
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
+                return ftl::yield<FenceResult>(Fence::NO_FENCE);
             });
 
     verify().execute().expectAFenceWasReturned();
@@ -3622,10 +3565,8 @@
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
             .Times(2)
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))))
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3653,8 +3594,7 @@
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
 
     verify().execute().expectAFenceWasReturned();
@@ -3693,9 +3633,8 @@
             .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()});
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
+                return ftl::yield<FenceResult>(Fence::NO_FENCE);
             });
 
     verify().execute().expectAFenceWasReturned();
@@ -3726,11 +3665,9 @@
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3807,8 +3744,7 @@
           : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
         auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
             EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _))
-                    .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
-                            {NO_ERROR, base::unique_fd()}))));
+                    .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
             return nextState<ExecuteState>();
         }
     };
@@ -4061,14 +3997,12 @@
                 .WillRepeatedly(Return());
         EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
         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()});
-                        });
+                .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                    const std::vector<renderengine::LayerSettings>&,
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    const bool, base::unique_fd&&) -> ftl::Future<FenceResult> {
+                    return ftl::yield<FenceResult>(Fence::NO_FENCE);
+                });
     }
 
     Layer mLayer1;
@@ -4133,8 +4067,7 @@
     // Must happen after setting the protected content state.
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     base::unique_fd fd;
     std::shared_ptr<renderengine::ExternalTexture> tex;
@@ -4226,8 +4159,7 @@
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     base::unique_fd fd;
     std::shared_ptr<renderengine::ExternalTexture> tex;
@@ -4250,8 +4182,7 @@
         EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
                 .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
         EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
-                .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
-                        {NO_ERROR, base::unique_fd()}))));
+                .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
                 .WillRepeatedly(Return(&mLayer.outputLayer));
@@ -4308,6 +4239,8 @@
 
     struct Layer {
         Layer() {
+            EXPECT_CALL(mOutputLayer, getOverrideCompositionSettings())
+                    .WillRepeatedly(Return(std::nullopt));
             EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
             EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
             EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
@@ -4405,23 +4338,18 @@
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
-    LayerFE::LayerSettings mShadowSettings;
-    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
-
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
-                    {mShadowSettings, mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[1].mLayerSettings)));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
     auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
                                                               kDisplayDataspace);
-    ASSERT_EQ(3u, requests.size());
+    ASSERT_EQ(2u, requests.size());
     EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
-    EXPECT_EQ(mShadowSettings, requests[1]);
-    EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
 
     // Check that a timestamp was set for the layers that generated requests
     EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
@@ -4438,27 +4366,21 @@
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, overridesBlur) {
-    LayerFE::LayerSettings mShadowSettings;
-    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
-
     mLayers[2].mOutputLayerState.overrideInfo.disableBackgroundBlur = true;
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[1].mLayerSettings)));
     EXPECT_CALL(*mLayers[2].mLayerFE,
-                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
                         LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly)))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
-                    {mShadowSettings, mLayers[2].mLayerSettings})));
-
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
     auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
                                                               kDisplayDataspace);
-    ASSERT_EQ(3u, requests.size());
+    ASSERT_EQ(2u, requests.size());
     EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
-    EXPECT_EQ(mShadowSettings, requests[1]);
-    EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
 
     // Check that a timestamp was set for the layers that generated requests
     EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
@@ -4480,8 +4402,8 @@
     mLayers[1].mLayerFEState.isOpaque = true;
     mLayers[2].mLayerFEState.isOpaque = true;
 
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
     auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
                                                               kDisplayDataspace);
@@ -4503,8 +4425,8 @@
     mLayers[1].mLayerFEState.isOpaque = false;
     mLayers[2].mLayerFEState.isOpaque = false;
 
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
     auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
                                                               kDisplayDataspace);
@@ -4562,10 +4484,10 @@
     mBlackoutSettings.alpha = 0.f;
     mBlackoutSettings.disableBlending = true;
 
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mBlackoutSettings})));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mBlackoutSettings)));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
     auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
                                                               kDisplayDataspace);
@@ -4620,12 +4542,12 @@
             kLayerWhitePointNits,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(
             mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
@@ -4674,12 +4596,12 @@
             kLayerWhitePointNits,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(
             mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
@@ -4728,12 +4650,12 @@
             kLayerWhitePointNits,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(
             mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
@@ -4781,12 +4703,12 @@
             kLayerWhitePointNits,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(
             mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
@@ -4832,12 +4754,12 @@
             kLayerWhitePointNits,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
                                                                 kDisplayDataspace));
@@ -5018,8 +4940,8 @@
 
     EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({leftLayer.mLayerSettings})));
+    EXPECT_CALL(*leftLayer.mLayerFE, prepareClientComposition(Eq(ByRef(leftLayerSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(leftLayer.mLayerSettings)));
 
     compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
             Region(Rect(1000, 0, 2000, 1000)),
@@ -5036,8 +4958,8 @@
 
     EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
+    EXPECT_CALL(*rightLayer.mLayerFE, prepareClientComposition(Eq(ByRef(rightLayerSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(rightLayer.mLayerSettings)));
 
     constexpr bool supportsProtectedContent = true;
     auto requests =
@@ -5075,8 +4997,8 @@
 
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mShadowSettings)));
 
     auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
                                                               kDisplayDataspace);
@@ -5093,9 +5015,6 @@
     const Region kPartialContentWithPartialShadowRegion =
             Region(kContentWithShadow).subtract(Rect(40, 40, 50, 80));
 
-    LayerFE::LayerSettings mShadowSettings;
-    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
-
     mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
     mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
 
@@ -5114,16 +5033,14 @@
 
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
-                    {mShadowSettings, mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
     auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
                                                               kDisplayDataspace);
-    ASSERT_EQ(2u, requests.size());
+    ASSERT_EQ(1u, requests.size());
 
-    EXPECT_EQ(mShadowSettings, requests[0]);
-    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 8a99e4e..d5d688e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -28,8 +28,6 @@
 #include <utils/Errors.h>
 #include <memory>
 
-#include "tests/TestUtils.h"
-
 namespace android::compositionengine {
 using namespace std::chrono_literals;
 
@@ -345,19 +343,18 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    clientCompList1[0].alpha = 0.5f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    clientComp1->alpha = 0.5f;
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    clientCompList2[0].alpha = 0.75f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    clientComp2->alpha = 0.75f;
 
-    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> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
@@ -365,18 +362,16 @@
         EXPECT_EQ(0.5f, layers[0].alpha);
         EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
-    EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
-            .WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
-            .WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false)))
+            .WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false)))
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = false;
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
     expectReadyBuffer(cachedSet);
 
     EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -397,19 +392,18 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    clientCompList1[0].alpha = 0.5f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    clientComp1->alpha = 0.5f;
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    clientCompList2[0].alpha = 0.75f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    clientComp2->alpha = 0.75f;
 
-    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> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
@@ -418,18 +412,16 @@
         EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
-    EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
-            .WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
-            .WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true)))
+            .WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true)))
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
     expectReadyBuffer(cachedSet);
 
     EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -450,34 +442,83 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
 
     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> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
                         mOutputState.displayBrightnessNits)))
-            .WillOnce(Return(clientCompList1));
+            .WillOnce(Return(clientComp1));
     EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
                         mOutputState.displayBrightnessNits)))
-            .WillOnce(Return(clientCompList2));
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
+    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, renderWhitePointNoColorTransform) {
+    // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+    // This is a duplicate of the "renderWhitePoint" test, but setting "deviceHandlesColorTransform"
+    // to false, in the render call.
+
+    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::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+
+    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&&) -> ftl::Future<FenceResult> {
+        EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
+    };
+
+    EXPECT_CALL(*layerFE1,
+                prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
+                        mOutputState.displayBrightnessNits)))
+            .WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2,
+                prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
+                        mOutputState.displayBrightnessNits)))
+            .WillOnce(Return(clientComp2));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    mOutputState.isSecure = true;
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false);
     expectReadyBuffer(cachedSet);
 
     EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -498,21 +539,20 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    clientCompList1[0].alpha = 0.5f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    clientComp1->alpha = 0.5f;
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    clientCompList2[0].alpha = 0.75f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    clientComp2->alpha = 0.75f;
 
     mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
 
-    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> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
@@ -521,13 +561,13 @@
         EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
-    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
     expectReadyBuffer(cachedSet);
 
     EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
@@ -722,28 +762,27 @@
 
     cachedSet.addHolePunchLayerIfFeasible(layer3, true);
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
-    clientCompList3.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp3;
+    clientComp3.emplace();
 
-    clientCompList3[0].source.buffer.buffer =
+    clientComp3->source.buffer.buffer =
             std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
                                                                       1ULL /* bufferId */,
                                                                       HAL_PIXEL_FORMAT_RGBA_8888,
                                                                       0ULL /*usage*/);
 
-    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
-    EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2));
+    EXPECT_CALL(*layerFE3, prepareClientComposition(_)).WillOnce(Return(clientComp3));
 
-    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> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         // 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);
@@ -763,15 +802,15 @@
             EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha);
         }
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
 }
 
 TEST_F(CachedSetTest, addHolePunch_noBuffer) {
-    // Same as addHolePunch, except that clientCompList3 does not contain a
+    // Same as addHolePunch, except that clientComp3 does not contain a
     // buffer. This imitates the case where the buffer had protected content, so
     // BufferLayer did not add it to the LayerSettings. This should not assert.
     mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
@@ -789,22 +828,21 @@
 
     cachedSet.addHolePunchLayerIfFeasible(layer3, true);
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
-    clientCompList3.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp3;
+    clientComp3.emplace();
 
-    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
-    EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2));
+    EXPECT_CALL(*layerFE3, prepareClientComposition(_)).WillOnce(Return(clientComp3));
 
-    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> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         // 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);
@@ -825,11 +863,11 @@
             EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha);
         }
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
 }
 
 TEST_F(CachedSetTest, append_removesHolePunch) {
@@ -923,40 +961,39 @@
 
     cachedSet.addBackgroundBlurLayer(layer3);
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
-    clientCompList3.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp3;
+    clientComp3.emplace();
 
-    clientCompList3[0].source.buffer.buffer =
+    clientComp3->source.buffer.buffer =
             std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
                                                                       1ULL /* bufferId */,
                                                                       HAL_PIXEL_FORMAT_RGBA_8888,
                                                                       0ULL /*usage*/);
 
     EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
                         compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
                                 Enabled)))
-            .WillOnce(Return(clientCompList1));
+            .WillOnce(Return(clientComp1));
     EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
                         compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
                                 Enabled)))
-            .WillOnce(Return(clientCompList2));
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(*layerFE3,
-                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
                         compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
                                 BackgroundBlurOnly)))
-            .WillOnce(Return(clientCompList3));
+            .WillOnce(Return(clientComp3));
 
-    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> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         // 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);
@@ -965,11 +1002,11 @@
         EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings.source.solidColor);
         EXPECT_EQ(0.0f, blurSettings.alpha);
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+    cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 200278c..86cfee6 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -27,8 +27,6 @@
 #include <renderengine/mock/RenderEngine.h>
 #include <chrono>
 
-#include "tests/TestUtils.h"
-
 namespace android::compositionengine {
 using namespace std::chrono_literals;
 using impl::planner::CachedSet;
@@ -124,12 +122,11 @@
         EXPECT_CALL(*testLayer->layerFE, getCompositionState)
                 .WillRepeatedly(Return(&testLayer->layerFECompositionState));
 
-        std::vector<LayerFE::LayerSettings> clientCompositionList = {
-                LayerFE::LayerSettings{},
-        };
+        std::optional<LayerFE::LayerSettings> clientComposition;
+        clientComposition.emplace();
 
-        EXPECT_CALL(*testLayer->layerFE, prepareClientCompositionList)
-                .WillRepeatedly(Return(clientCompositionList));
+        EXPECT_CALL(*testLayer->layerFE, prepareClientComposition)
+                .WillRepeatedly(Return(clientComposition));
         EXPECT_CALL(testLayer->outputLayer, getLayerFE)
                 .WillRepeatedly(ReturnRef(*testLayer->layerFE));
         EXPECT_CALL(testLayer->outputLayer, getState)
@@ -160,25 +157,24 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // same geometry, update the internal layer stack
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 }
 
 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(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -188,7 +184,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
     EXPECT_NE(nullptr, buffer);
@@ -223,7 +219,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 }
 
 TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
@@ -285,7 +281,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -390,7 +386,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -424,12 +420,11 @@
     layerState1->resetFramesSinceBufferUpdate();
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -438,7 +433,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -448,12 +443,11 @@
     mTime += 200ms;
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -462,7 +456,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -501,12 +495,11 @@
     layerState3->resetFramesSinceBufferUpdate();
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -516,13 +509,12 @@
 
     // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -535,7 +527,7 @@
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180);
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -546,12 +538,11 @@
     layerState3->incrementFramesSinceBufferUpdate();
     mTime += 200ms;
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -563,7 +554,7 @@
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270);
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -602,9 +593,8 @@
 
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -617,7 +607,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -638,16 +628,15 @@
 
     EXPECT_CALL(*mTestLayers[2]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
 
-    std::vector<LayerFE::LayerSettings> clientCompositionList = {
-            LayerFE::LayerSettings{},
-    };
-    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+    std::optional<LayerFE::LayerSettings> clientComposition;
+    clientComposition.emplace();
+    clientComposition->source.buffer.buffer = std::make_shared<
             renderengine::impl::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer,
                                                  mRenderEngine,
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          READABLE);
-    EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(clientCompositionList));
+    EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientComposition(_))
+            .WillOnce(Return(clientComposition));
 
     const std::vector<const LayerState*> layers = {
             layerState1.get(),
@@ -668,9 +657,8 @@
 
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -683,7 +671,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -712,16 +700,15 @@
 
     EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
 
-    std::vector<LayerFE::LayerSettings> clientCompositionList = {
-            LayerFE::LayerSettings{},
-    };
-    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+    std::optional<LayerFE::LayerSettings> clientComposition;
+    clientComposition.emplace();
+    clientComposition->source.buffer.buffer = std::make_shared<
             renderengine::impl::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
                                                  mRenderEngine,
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          READABLE);
-    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(clientCompositionList));
+    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientComposition(_))
+            .WillOnce(Return(clientComposition));
 
     const std::vector<const LayerState*> layers = {
             layerState0.get(),
@@ -742,9 +729,8 @@
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer0);
@@ -754,7 +740,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer0); // got overridden
     EXPECT_EQ(nullptr, overrideBuffer1); // did not
@@ -784,16 +770,15 @@
 
     EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
 
-    std::vector<LayerFE::LayerSettings> clientCompositionList = {
-            LayerFE::LayerSettings{},
-    };
-    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+    std::optional<LayerFE::LayerSettings> clientComposition;
+    clientComposition.emplace();
+    clientComposition->source.buffer.buffer = std::make_shared<
             renderengine::impl::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
                                                  mRenderEngine,
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          READABLE);
-    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(clientCompositionList));
+    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientComposition(_))
+            .WillOnce(Return(clientComposition));
 
     const std::vector<const LayerState*> layers = {
             layerState0.get(),
@@ -814,9 +799,8 @@
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer0);
@@ -826,7 +810,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer0); // got overridden
     EXPECT_EQ(nullptr, overrideBuffer1); // did not
@@ -866,13 +850,12 @@
 
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -882,7 +865,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
     EXPECT_EQ(nullptr, overrideBuffer3);
@@ -912,13 +895,12 @@
 
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillRepeatedly(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillRepeatedly(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -929,7 +911,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
     }
@@ -966,13 +948,12 @@
 
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -982,7 +963,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, blurOverrideBuffer);
     EXPECT_NE(nullptr, overrideBuffer3);
@@ -1015,13 +996,12 @@
 
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     const auto& cachedSet = mFlattener->getNewCachedSetForTesting();
     ASSERT_NE(std::nullopt, cachedSet);
@@ -1035,7 +1015,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer2, overrideBuffer1);
     EXPECT_EQ(nullptr, blurOverrideBuffer);
@@ -1058,13 +1038,12 @@
     mTime += 200ms;
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -1072,12 +1051,12 @@
     // 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);
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We provide the override buffer now that it's rendered
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer2, overrideBuffer1);
@@ -1121,15 +1100,16 @@
         EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
         mFlattener->renderCachedSets(mOutputState,
                                      std::chrono::steady_clock::now() -
-                                             (kCachedSetRenderDuration + 10ms));
+                                             (kCachedSetRenderDuration + 10ms),
+                                     true);
     }
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState,
                                  std::chrono::steady_clock::now() -
-                                         (kCachedSetRenderDuration + 10ms));
+                                         (kCachedSetRenderDuration + 10ms),
+                                 true);
 }
 
 TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
@@ -1161,9 +1141,8 @@
 
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1176,7 +1155,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1212,9 +1191,8 @@
 
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1227,7 +1205,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1263,9 +1241,8 @@
 
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1278,7 +1255,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -1317,9 +1294,8 @@
 
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1333,7 +1309,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -1370,9 +1346,8 @@
 
     // 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);
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -1385,7 +1360,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/services/surfaceflinger/Display/DisplayMap.h
similarity index 60%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to services/surfaceflinger/Display/DisplayMap.h
index 9fac3e8..baf0da9 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/services/surfaceflinger/Display/DisplayMap.h
@@ -14,10 +14,18 @@
  * limitations under the License.
  */
 
-package android.gui;
+#pragma once
 
-/** @hide */
-parcelable MirrorSurfaceResult {
-    IBinder handle;
-    int layerId;
-}
+#include <ftl/small_map.h>
+
+namespace android::display {
+
+// The static capacities were chosen to exceed a typical number of physical and/or virtual displays.
+
+template <typename Key, typename Value>
+using DisplayMap = ftl::SmallMap<Key, Value, 5>;
+
+template <typename Key, typename Value>
+using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>;
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.cpp b/services/surfaceflinger/Display/DisplaySnapshot.cpp
new file mode 100644
index 0000000..0c7a58e
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplaySnapshot.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+
+#include <ftl/algorithm.h>
+#include <ftl/enum.h>
+#include <ui/DebugUtils.h>
+
+#include "DisplaySnapshot.h"
+
+namespace android::display {
+
+DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId,
+                                 ui::DisplayConnectionType connectionType,
+                                 DisplayModes&& displayModes, ui::ColorModes&& colorModes,
+                                 std::optional<DeviceProductInfo>&& deviceProductInfo)
+      : mDisplayId(displayId),
+        mConnectionType(connectionType),
+        mDisplayModes(std::move(displayModes)),
+        mColorModes(std::move(colorModes)),
+        mDeviceProductInfo(std::move(deviceProductInfo)) {}
+
+std::optional<DisplayModeId> DisplaySnapshot::translateModeId(hal::HWConfigId hwcId) const {
+    return ftl::find_if(mDisplayModes,
+                        [hwcId](const DisplayModes::value_type& pair) {
+                            return pair.second->getHwcId() == hwcId;
+                        })
+            .transform(&ftl::to_key<DisplayModes>);
+}
+
+ui::ColorModes DisplaySnapshot::filterColorModes(bool supportsWideColor) const {
+    ui::ColorModes modes = mColorModes;
+
+    // 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 (mConnectionType == ui::DisplayConnectionType::Internal && !supportsWideColor) {
+        const auto it = std::remove_if(modes.begin(), modes.end(), ui::isWideColorMode);
+        modes.erase(it, modes.end());
+    }
+
+    return modes;
+}
+
+void DisplaySnapshot::dump(utils::Dumper& dumper) const {
+    using namespace std::string_view_literals;
+
+    dumper.dump("connectionType"sv, ftl::enum_string(mConnectionType));
+
+    dumper.dump("colorModes"sv);
+    {
+        utils::Dumper::Indent indent(dumper);
+        for (const auto mode : mColorModes) {
+            dumper.dump({}, decodeColorMode(mode));
+        }
+    }
+
+    dumper.dump("deviceProductInfo"sv, mDeviceProductInfo);
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.h b/services/surfaceflinger/Display/DisplaySnapshot.h
new file mode 100644
index 0000000..23471f5
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplaySnapshot.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <ui/ColorMode.h>
+#include <ui/DisplayId.h>
+#include <ui/StaticDisplayInfo.h>
+
+#include "DisplayHardware/DisplayMode.h"
+#include "Utils/Dumper.h"
+
+namespace android::display {
+
+// Immutable state of a physical display, captured on hotplug.
+class DisplaySnapshot {
+public:
+    DisplaySnapshot(PhysicalDisplayId, ui::DisplayConnectionType, DisplayModes&&, ui::ColorModes&&,
+                    std::optional<DeviceProductInfo>&&);
+
+    DisplaySnapshot(const DisplaySnapshot&) = delete;
+    DisplaySnapshot(DisplaySnapshot&&) = default;
+
+    PhysicalDisplayId displayId() const { return mDisplayId; }
+    ui::DisplayConnectionType connectionType() const { return mConnectionType; }
+
+    std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
+
+    const auto& displayModes() const { return mDisplayModes; }
+    const auto& colorModes() const { return mColorModes; }
+    const auto& deviceProductInfo() const { return mDeviceProductInfo; }
+
+    ui::ColorModes filterColorModes(bool supportsWideColor) const;
+
+    void dump(utils::Dumper&) const;
+
+private:
+    const PhysicalDisplayId mDisplayId;
+    const ui::DisplayConnectionType mConnectionType;
+
+    // Effectively const except in move constructor.
+    DisplayModes mDisplayModes;
+    ui::ColorModes mColorModes;
+    std::optional<DeviceProductInfo> mDeviceProductInfo;
+};
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/PhysicalDisplay.h b/services/surfaceflinger/Display/PhysicalDisplay.h
new file mode 100644
index 0000000..cba1014
--- /dev/null
+++ b/services/surfaceflinger/Display/PhysicalDisplay.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <utility>
+
+#include <binder/IBinder.h>
+#include <ui/DisplayId.h>
+#include <utils/StrongPointer.h>
+
+#include "DisplayMap.h"
+#include "DisplaySnapshot.h"
+
+namespace android::display {
+
+// TODO(b/229877597): Replace with AIDL type.
+using DisplayToken = IBinder;
+
+class PhysicalDisplay {
+public:
+    template <typename... Args>
+    PhysicalDisplay(sp<DisplayToken> token, Args&&... args)
+          : mToken(std::move(token)), mSnapshot(std::forward<Args>(args)...) {}
+
+    PhysicalDisplay(const PhysicalDisplay&) = delete;
+    PhysicalDisplay(PhysicalDisplay&&) = default;
+
+    const sp<DisplayToken>& token() const { return mToken; }
+    const DisplaySnapshot& snapshot() const { return mSnapshot; }
+
+    // Transformers for PhysicalDisplays::get.
+
+    using SnapshotRef = std::reference_wrapper<const DisplaySnapshot>;
+    SnapshotRef snapshotRef() const { return std::cref(mSnapshot); }
+
+    bool isInternal() const {
+        return mSnapshot.connectionType() == ui::DisplayConnectionType::Internal;
+    }
+
+    // Predicate for ftl::find_if on PhysicalDisplays.
+    static constexpr auto hasToken(const sp<DisplayToken>& token) {
+        return [&token](const std::pair<const PhysicalDisplayId, PhysicalDisplay>& pair) {
+            return pair.second.token() == token;
+        };
+    }
+
+private:
+    const sp<DisplayToken> mToken;
+
+    // Effectively const except in move constructor.
+    DisplaySnapshot mSnapshot;
+};
+
+using PhysicalDisplays = PhysicalDisplayMap<PhysicalDisplayId, PhysicalDisplay>;
+
+// Combinator for ftl::Optional<PhysicalDisplayId>::and_then.
+constexpr auto getPhysicalDisplay(const PhysicalDisplays& displays) {
+    return [&](PhysicalDisplayId id) { return displays.get(id); };
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index bff301e..c63d57f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -39,6 +39,7 @@
 #include <system/window.h>
 #include <ui/GraphicTypes.h>
 
+#include "Display/DisplaySnapshot.h"
 #include "DisplayDevice.h"
 #include "Layer.h"
 #include "RefreshRateOverlay.h"
@@ -63,12 +64,10 @@
         mHwComposer(args.hwComposer),
         mDisplayToken(args.displayToken),
         mSequenceId(args.sequenceId),
-        mConnectionType(args.connectionType),
         mCompositionDisplay{args.compositionDisplay},
         mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
         mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
         mPhysicalOrientation(args.physicalOrientation),
-        mSupportedModes(std::move(args.supportedModes)),
         mIsPrimary(args.isPrimary),
         mRefreshRateConfigs(std::move(args.refreshRateConfigs)) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
@@ -104,7 +103,7 @@
 
     mCompositionDisplay->getRenderSurface()->initialize();
 
-    setPowerMode(args.initialPowerMode);
+    if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value());
 
     // initialize the display orientation transform.
     setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
@@ -132,10 +131,6 @@
     }
 }
 
-void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
-    mDeviceProductInfo = std::move(info);
-}
-
 auto DisplayDevice::getInputInfo() const -> InputInfo {
     gui::DisplayInfo info;
     info.displayId = getLayerStack().id;
@@ -169,32 +164,46 @@
 }
 
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
+    if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
+        if (mStagedBrightness && mBrightness != *mStagedBrightness) {
+            getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+            mBrightness = *mStagedBrightness;
+        }
+        mStagedBrightness = std::nullopt;
+        getCompositionDisplay()->applyDisplayBrightness(true);
+    }
+
     mPowerMode = mode;
-    getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
+
+    getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
+                                                   *mPowerMode != hal::PowerMode::OFF);
 }
 
 void DisplayDevice::enableLayerCaching(bool enable) {
     getCompositionDisplay()->setLayerCachingEnabled(enable);
 }
 
-hal::PowerMode DisplayDevice::getPowerMode() const {
+std::optional<hal::PowerMode> DisplayDevice::getPowerMode() const {
     return mPowerMode;
 }
 
 bool DisplayDevice::isPoweredOn() const {
-    return mPowerMode != hal::PowerMode::OFF;
+    return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
 }
 
-void DisplayDevice::setActiveMode(DisplayModeId id) {
-    const auto mode = getMode(id);
-    LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported.");
-    ATRACE_INT(mActiveModeFPSTrace.c_str(), mode->getFps().getIntValue());
-    mActiveMode = mode;
-    if (mRefreshRateConfigs) {
-        mRefreshRateConfigs->setActiveModeId(mActiveMode->getId());
-    }
+void DisplayDevice::setActiveMode(DisplayModeId modeId, const display::DisplaySnapshot& snapshot) {
+    const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
+            [](const DisplayModePtr& mode) { return mode->getFps(); });
+
+    LOG_ALWAYS_FATAL_IF(!fpsOpt, "Unknown mode");
+    const Fps fps = *fpsOpt;
+
+    ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue());
+
+    mRefreshRateConfigs->setActiveModeId(modeId);
+
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(mActiveMode->getFps());
+        mRefreshRateOverlay->changeRefreshRate(fps);
     }
 }
 
@@ -207,36 +216,12 @@
               to_string(getId()).c_str());
         return BAD_VALUE;
     }
-    mNumModeSwitchesInPolicy++;
     mUpcomingActiveMode = info;
     ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
     return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
                                                     constraints, outTimeline);
 }
 
-const DisplayModePtr& DisplayDevice::getActiveMode() const {
-    return mActiveMode;
-}
-
-const DisplayModes& DisplayDevice::getSupportedModes() const {
-    return mSupportedModes;
-}
-
-DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const {
-    const DisplayModePtr nullMode;
-    return mSupportedModes.get(modeId).value_or(std::cref(nullMode));
-}
-
-std::optional<DisplayModeId> DisplayDevice::translateModeId(hal::HWConfigId hwcId) const {
-    const auto it =
-            std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
-                         [hwcId](const auto& pair) { return pair.second->getHwcId() == hwcId; });
-    if (it != mSupportedModes.end()) {
-        return it->second->getId();
-    }
-    return {};
-}
-
 nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
     const auto physicalId = getPhysicalId();
     if (!mHwComposer.isConnected(physicalId)) {
@@ -249,27 +234,17 @@
         return vsyncPeriod;
     }
 
-    return getActiveMode()->getFps().getPeriodNsecs();
-}
-
-nsecs_t DisplayDevice::getRefreshTimestamp() const {
-    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    const auto vsyncPeriodNanos = getVsyncPeriodFromHWC();
-    return now - ((now - mLastHwVsync) % vsyncPeriodNanos);
-}
-
-void DisplayDevice::onVsync(nsecs_t timestamp) {
-    mLastHwVsync = timestamp;
+    return refreshRateConfigs().getActiveModePtr()->getVsyncPeriod();
 }
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
     return mCompositionDisplay->getState().dataspace;
 }
 
-void DisplayDevice::setLayerStack(ui::LayerStack stack) {
-    mCompositionDisplay->setLayerFilter({stack, isInternal()});
+void DisplayDevice::setLayerFilter(ui::LayerFilter filter) {
+    mCompositionDisplay->setLayerFilter(filter);
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->setLayerStack(stack);
+        mRefreshRateOverlay->setLayerStack(filter.layerStack);
     }
 }
 
@@ -321,8 +296,10 @@
 }
 
 void DisplayDevice::persistBrightness(bool needsComposite) {
-    if (needsComposite && mStagedBrightness && mBrightness != *mStagedBrightness) {
-        getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+    if (mStagedBrightness && mBrightness != *mStagedBrightness) {
+        if (needsComposite) {
+            getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+        }
         mBrightness = *mStagedBrightness;
     }
     mStagedBrightness = std::nullopt;
@@ -341,11 +318,7 @@
 
     std::string name = "Display "s + to_string(getId()) + " ("s;
 
-    if (mConnectionType) {
-        name += isInternal() ? "internal"s : "external"s;
-    } else {
-        name += "virtual"s;
-    }
+    name += isVirtual() ? "virtual"s : "physical"s;
 
     if (isPrimary()) {
         name += ", primary"s;
@@ -354,26 +327,16 @@
     return name + ", \""s + mDisplayName + "\")"s;
 }
 
-void DisplayDevice::dump(std::string& result) const {
-    using namespace std::string_literals;
+void DisplayDevice::dump(utils::Dumper& dumper) const {
+    using namespace std::string_view_literals;
 
-    result += getDebugName();
+    dumper.dump({}, getDebugName());
 
-    if (!isVirtual()) {
-        result += "\n   deviceProductInfo="s;
-        if (mDeviceProductInfo) {
-            mDeviceProductInfo->dump(result);
-        } else {
-            result += "{}"s;
-        }
-    }
-
-    result += "\n   powerMode="s;
-    result += to_string(mPowerMode);
-    result += '\n';
+    utils::Dumper::Indent indent(dumper);
+    dumper.dump("powerMode"sv, mPowerMode);
 
     if (mRefreshRateConfigs) {
-        mRefreshRateConfigs->dump(result);
+        mRefreshRateConfigs->dump(dumper);
     }
 }
 
@@ -471,7 +434,7 @@
     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
-    mRefreshRateOverlay->changeRefreshRate(getActiveMode()->getFps());
+    mRefreshRateOverlay->changeRefreshRate(getActiveMode().getFps());
 }
 
 bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
@@ -512,7 +475,7 @@
     }
 
     // Check if we are already at the desired mode
-    if (getActiveMode()->getId() == info.mode->getId()) {
+    if (refreshRateConfigs().getActiveModePtr()->getId() == info.mode->getId()) {
         return false;
     }
 
@@ -534,27 +497,6 @@
     mDesiredActiveModeChanged = false;
 }
 
-status_t DisplayDevice::setRefreshRatePolicy(
-        const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
-    const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy();
-    const status_t setPolicyResult = overridePolicy
-            ? mRefreshRateConfigs->setOverridePolicy(policy)
-            : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
-
-    if (setPolicyResult == OK) {
-        const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0);
-
-        ALOGI("Display %s policy changed\n"
-              "Previous: {%s}\n"
-              "Current:  {%s}\n"
-              "%d mode changes were performed under the previous policy",
-              to_string(getId()).c_str(), oldPolicy.toString().c_str(),
-              policy ? policy->toString().c_str() : "null", numModeChanges);
-    }
-
-    return setPolicyResult;
-}
-
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
 
 }  // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 10fc095..06a812b 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -47,6 +47,7 @@
 #include "Scheduler/RefreshRateConfigs.h"
 #include "ThreadContext.h"
 #include "TracedOrdinal.h"
+#include "Utils/Dumper.h"
 
 namespace android {
 
@@ -65,6 +66,10 @@
 class DisplaySurface;
 } // namespace compositionengine
 
+namespace display {
+class DisplaySnapshot;
+} // namespace display
+
 class DisplayDevice : public RefBase {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
@@ -80,11 +85,8 @@
         return mCompositionDisplay;
     }
 
-    std::optional<ui::DisplayConnectionType> getConnectionType() const { return mConnectionType; }
-
-    bool isVirtual() const { return !mConnectionType; }
+    bool isVirtual() const { return VirtualDisplayId::tryCast(getId()).has_value(); }
     bool isPrimary() const { return mIsPrimary; }
-    bool isInternal() const { return mConnectionType == ui::DisplayConnectionType::Internal; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
@@ -94,7 +96,7 @@
     int getHeight() const;
     ui::Size getSize() const { return {getWidth(), getHeight()}; }
 
-    void setLayerStack(ui::LayerStack);
+    void setLayerFilter(ui::LayerFilter);
     void setDisplaySize(int width, int height);
     void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
     void stageBrightness(float brightness) REQUIRES(kMainThreadContext);
@@ -164,11 +166,6 @@
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
-    void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
-    const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
-        return mDeviceProductInfo;
-    }
-
     struct InputInfo {
         gui::DisplayInfo info;
         ui::Transform transform;
@@ -181,7 +178,7 @@
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
-    hardware::graphics::composer::hal::PowerMode getPowerMode() const;
+    std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const;
     void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
     bool isPoweredOn() const;
 
@@ -193,8 +190,6 @@
     /* ------------------------------------------------------------------------
      * Display mode management.
      */
-    const DisplayModePtr& getActiveMode() const;
-
     struct ActiveModeInfo {
         DisplayModePtr mode;
         scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None;
@@ -211,24 +206,18 @@
         return mUpcomingActiveMode;
     }
 
-    void setActiveMode(DisplayModeId) REQUIRES(kMainThreadContext);
+    const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext) {
+        return mRefreshRateConfigs->getActiveMode();
+    }
+
+    // Precondition: DisplaySnapshot must contain a mode with DisplayModeId.
+    void setActiveMode(DisplayModeId, const display::DisplaySnapshot&) REQUIRES(kMainThreadContext);
+
     status_t initiateModeChange(const ActiveModeInfo&,
                                 const hal::VsyncPeriodChangeConstraints& constraints,
                                 hal::VsyncPeriodChangeTimeline* outTimeline)
             REQUIRES(kMainThreadContext);
 
-    // Return the immutable list of supported display modes. The HWC may report different modes
-    // after a hotplug reconnect event, in which case the DisplayDevice object will be recreated.
-    // Hotplug reconnects are common for external displays.
-    const DisplayModes& getSupportedModes() const;
-
-    // Returns nullptr if the given mode ID is not supported. A previously
-    // supported mode may be no longer supported for some devices like TVs and
-    // set-top boxes after a hotplug reconnect.
-    DisplayModePtr getMode(DisplayModeId) const;
-
-    std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
-
     // Returns the refresh rate configs for this display.
     scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
 
@@ -240,18 +229,12 @@
     }
 
     // Enables an overlay to be displayed with the current refresh rate
-    void enableRefreshRateOverlay(bool enable, bool showSpinner);
+    void enableRefreshRateOverlay(bool enable, bool showSpinner) REQUIRES(kMainThreadContext);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
     void animateRefreshRateOverlay();
 
-    void onVsync(nsecs_t timestamp);
     nsecs_t getVsyncPeriodFromHWC() const;
-    nsecs_t getRefreshTimestamp() const;
-
-    status_t setRefreshRatePolicy(
-            const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
-            bool overridePolicy);
 
     // release HWC resources (if any) for removable displays
     void disconnect();
@@ -260,14 +243,13 @@
      * Debugging
      */
     std::string getDebugName() const;
-    void dump(std::string& result) const;
+    void dump(utils::Dumper&) const;
 
 private:
     const sp<SurfaceFlinger> mFlinger;
     HWComposer& mHwComposer;
     const wp<IBinder> mDisplayToken;
     const int32_t mSequenceId;
-    const std::optional<ui::DisplayConnectionType> mConnectionType;
 
     const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
@@ -280,22 +262,17 @@
 
     static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
 
-    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;
+    // Allow nullopt as initial power mode.
+    std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode;
 
-    std::atomic<nsecs_t> mLastHwVsync = 0;
+    std::optional<float> mStagedBrightness;
+    float mBrightness = -1.f;
 
     // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
 
     uint32_t mFlags = 0;
 
-    std::optional<DeviceProductInfo> mDeviceProductInfo;
-
     std::vector<ui::Hdr> mOverrideHdrTypes;
 
     std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
@@ -306,21 +283,16 @@
     TracedOrdinal<bool> mDesiredActiveModeChanged
             GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
     ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
-
-    std::atomic_int mNumModeSwitchesInPolicy = 0;
 };
 
 struct DisplayDeviceState {
     struct Physical {
         PhysicalDisplayId id;
-        ui::DisplayConnectionType type;
         hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
-        std::optional<DeviceProductInfo> deviceProductInfo;
-        DisplayModes supportedModes;
         DisplayModePtr activeMode;
 
         bool operator==(const Physical& other) const {
-            return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
+            return id == other.id && hwcDisplayId == other.hwcDisplayId;
         }
     };
 
@@ -356,7 +328,6 @@
     std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs;
 
     int32_t sequenceId{0};
-    std::optional<ui::DisplayConnectionType> connectionType;
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
     sp<compositionengine::DisplaySurface> displaySurface;
@@ -365,10 +336,8 @@
     HdrCapabilities hdrCapabilities;
     int32_t supportedPerFrameMetadata{0};
     std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
-    hardware::graphics::composer::hal::PowerMode initialPowerMode{
-            hardware::graphics::composer::hal::PowerMode::ON};
+    std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode;
     bool isPrimary{false};
-    DisplayModes supportedModes;
     DisplayModeId activeModeId;
 };
 
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 79dcd15..3651231 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -264,17 +264,21 @@
     }
 
     std::string str;
+    // Use other thread to read pipe to prevent
+    // pipe is full, making HWC be blocked in writing.
+    std::thread t([&]() {
+        base::ReadFdToString(pipefds[0], &str);
+    });
     const auto status = mAidlComposer->dump(pipefds[1], /*args*/ nullptr, /*numArgs*/ 0);
     // Close the write-end of the pipe to make sure that when reading from the
     // read-end we will get eof instead of blocking forever
     close(pipefds[1]);
 
-    if (status == STATUS_OK) {
-        base::ReadFdToString(pipefds[0], &str);
-    } else {
+    if (status != STATUS_OK) {
         ALOGE("dumpDebugInfo: dump failed: %d", status);
     }
 
+    t.join();
     close(pipefds[0]);
     return str;
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 486eaf8..96399e2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -70,7 +70,7 @@
 struct ComposerCallback {
     virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0;
     virtual void onComposerHalRefresh(hal::HWDisplayId) = 0;
-    virtual void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+    virtual void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                                     std::optional<hal::VsyncPeriodNanos>) = 0;
     virtual void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
                                                        const hal::VsyncPeriodChangeTimeline&) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index a6aee1f..0a4ad97 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -144,7 +144,7 @@
     return mUpdateDeviceProductInfoOnHotplugReconnect;
 }
 
-bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) {
+bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, nsecs_t timestamp) {
     const auto displayId = toPhysicalDisplayId(hwcDisplayId);
     if (!displayId) {
         LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
@@ -160,13 +160,13 @@
         // with the same timestamp when turning the display off and on. This
         // is a bug in the HWC implementation, but filter the extra events
         // out here so they don't cause havoc downstream.
-        if (timestamp == displayData.lastHwVsync) {
+        if (timestamp == displayData.lastPresentTimestamp) {
             ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
                   to_string(*displayId).c_str(), timestamp);
             return false;
         }
 
-        displayData.lastHwVsync = timestamp;
+        displayData.lastPresentTimestamp = timestamp;
     }
 
     const auto tag = "HW_VSYNC_" + to_string(*displayId);
@@ -252,11 +252,7 @@
 }
 
 bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
-    if (mDisplayData.count(displayId)) {
-        return mDisplayData.at(displayId).hwcDisplay->isConnected();
-    }
-
-    return false;
+    return mDisplayData.count(displayId) && mDisplayData.at(displayId).hwcDisplay->isConnected();
 }
 
 std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const {
@@ -286,17 +282,12 @@
 
 std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt);
-
     const auto hwcId = *fromPhysicalDisplayId(displayId);
-    ALOGV("[%" PRIu64 "] getActiveMode", hwcId);
+
     hal::HWConfigId configId;
-    auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId));
+    const auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId));
 
-    if (error == hal::Error::BAD_CONFIG) {
-        LOG_DISPLAY_ERROR(displayId, "No active mode");
-        return std::nullopt;
-    }
-
+    RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, std::nullopt);
     return configId;
 }
 
@@ -494,6 +485,11 @@
     return mDisplayData.at(displayId).lastPresentFence;
 }
 
+nsecs_t HWComposer::getPresentTimestamp(PhysicalDisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+    return mDisplayData.at(displayId).lastPresentTimestamp;
+}
+
 sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     const auto& displayFences = mDisplayData.at(displayId).releaseFences;
@@ -951,18 +947,15 @@
         return {};
     }
 
-    // The display will later be destroyed by a call to
-    // destroyDisplay(). For now we just mark it disconnected.
-    if (isConnected(*displayId)) {
-        mDisplayData[*displayId].hwcDisplay->setConnected(false);
-    } else {
+    if (!isConnected(*displayId)) {
         LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected");
+        return {};
     }
-    // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
-    // via SurfaceFlinger's onHotplugReceived callback handling
-    return DisplayIdentificationInfo{.id = *displayId,
-                                     .name = std::string(),
-                                     .deviceProductInfo = std::nullopt};
+
+    // The display will later be destroyed by a call to HWComposer::disconnectDisplay. For now, mark
+    // it as disconnected.
+    mDisplayData.at(*displayId).hwcDisplay->setConnected(false);
+    return DisplayIdentificationInfo{.id = *displayId};
 }
 
 void HWComposer::loadCapabilities() {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 92a8f30..6c43d8b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -160,8 +160,9 @@
     // reset state when a display is disconnected
     virtual void disconnectDisplay(HalDisplayId) = 0;
 
-    // get the present fence received from the last call to present.
+    // Get the present fence/timestamp received from the last call to present.
     virtual sp<Fence> getPresentFence(HalDisplayId) const = 0;
+    virtual nsecs_t getPresentTimestamp(PhysicalDisplayId) const = 0;
 
     // Get last release fence for the given layer
     virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0;
@@ -214,7 +215,7 @@
     // TODO(b/157555476): Remove when the framework has proper support for headless mode
     virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0;
 
-    virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
+    virtual bool onVsync(hal::HWDisplayId, nsecs_t timestamp) = 0;
     virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
 
     virtual bool isConnected(PhysicalDisplayId) const = 0;
@@ -343,8 +344,9 @@
     // reset state when a display is disconnected
     void disconnectDisplay(HalDisplayId) override;
 
-    // get the present fence received from the last call to present.
+    // Get the present fence/timestamp received from the last call to present.
     sp<Fence> getPresentFence(HalDisplayId) const override;
+    nsecs_t getPresentTimestamp(PhysicalDisplayId) const override;
 
     // Get last release fence for the given layer
     sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override;
@@ -387,7 +389,7 @@
 
     bool updatesDeviceProductInfoOnHotplugReconnect() const override;
 
-    bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
+    bool onVsync(hal::HWDisplayId, nsecs_t timestamp) override;
     void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
 
     bool isConnected(PhysicalDisplayId) const override;
@@ -456,7 +458,10 @@
 
     struct DisplayData {
         std::unique_ptr<HWC2::Display> hwcDisplay;
+
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
+        nsecs_t lastPresentTimestamp = 0;
+
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
 
         bool validateWasSkipped;
@@ -466,8 +471,6 @@
 
         std::mutex vsyncEnabledLock;
         hal::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = hal::Vsync::DISABLE;
-
-        nsecs_t lastHwVsync = 0;
     };
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 4737034..33a7bca 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -177,6 +177,9 @@
     return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error));
 }
 
+// For utils::Dumper ADL.
+namespace hardware::graphics::composer::V2_2 {
+
 inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) {
     switch (mode) {
         case hardware::graphics::composer::hal::PowerMode::OFF:
@@ -194,6 +197,8 @@
     }
 }
 
+} // namespace hardware::graphics::composer::V2_2
+
 inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) {
     switch (vsync) {
         case hardware::graphics::composer::hal::Vsync::ENABLE:
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index ea71a32..cb2c8c5 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -59,8 +59,6 @@
 using android::hardware::power::Mode;
 using android::hardware::power::WorkDuration;
 
-using scheduler::OneShotTimer;
-
 PowerAdvisor::~PowerAdvisor() = default;
 
 namespace {
@@ -196,7 +194,7 @@
     return mPowerHintSessionRunning;
 }
 
-void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) {
+void PowerAdvisor::setTargetWorkDuration(Duration targetDuration) {
     if (!usePowerHintSession()) {
         ALOGV("Power hint session target duration cannot be set, skipping");
         return;
@@ -215,13 +213,13 @@
         ALOGV("Actual work duration power hint cannot be sent, skipping");
         return;
     }
-    const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false);
+    const std::optional<Duration> actualDuration = estimateWorkDuration(false);
     if (actualDuration.has_value()) {
         std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper != nullptr) {
-            halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(),
-                                               systemTime());
+            halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin,
+                                               TimePoint::now());
         }
     }
 }
@@ -232,14 +230,14 @@
         return;
     }
 
-    const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true);
+    const std::optional<Duration> predictedDuration = estimateWorkDuration(true);
 
     if (predictedDuration.has_value()) {
         std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper != nullptr) {
-            halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(),
-                                               systemTime());
+            halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin,
+                                               TimePoint::now());
         }
     }
 }
@@ -281,22 +279,22 @@
                 }
             }
             displayData.lastValidGpuStartTime = displayData.gpuStartTime;
-            displayData.lastValidGpuEndTime = signalTime;
+            displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime);
         }
     }
     displayData.gpuEndFenceTime = std::move(fenceTime);
-    displayData.gpuStartTime = systemTime();
+    displayData.gpuStartTime = TimePoint::now();
 }
 
-void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
-                                        nsecs_t validateEndTime) {
+void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
+                                        TimePoint validateEndTime) {
     DisplayTimingData& displayData = mDisplayTimingData[displayId];
     displayData.hwcValidateStartTime = validateStartTime;
     displayData.hwcValidateEndTime = validateEndTime;
 }
 
-void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
-                                       nsecs_t presentEndTime) {
+void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
+                                       TimePoint presentEndTime) {
     DisplayTimingData& displayData = mDisplayTimingData[displayId];
     displayData.hwcPresentStartTime = presentStartTime;
     displayData.hwcPresentEndTime = presentEndTime;
@@ -311,43 +309,41 @@
     mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition;
 }
 
-void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) {
+void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) {
     mExpectedPresentTimes.append(expectedPresentTime);
 }
 
-void PowerAdvisor::setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) {
+void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) {
     mLastSfPresentEndTime = presentEndTime;
     mLastPresentFenceTime = presentFenceTime;
 }
 
-void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) {
+void PowerAdvisor::setFrameDelay(Duration frameDelayDuration) {
     mFrameDelayDuration = frameDelayDuration;
 }
 
-void PowerAdvisor::setHwcPresentDelayedTime(
-        DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) {
-    mDisplayTimingData[displayId].hwcPresentDelayedTime =
-            (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime();
+void PowerAdvisor::setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) {
+    mDisplayTimingData[displayId].hwcPresentDelayedTime = earliestFrameStartTime;
 }
 
-void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) {
+void PowerAdvisor::setCommitStart(TimePoint commitStartTime) {
     mCommitStartTimes.append(commitStartTime);
 }
 
-void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) {
-    mLastPostcompDuration = compositeEnd - mLastSfPresentEndTime;
+void PowerAdvisor::setCompositeEnd(TimePoint compositeEndTime) {
+    mLastPostcompDuration = compositeEndTime - mLastSfPresentEndTime;
 }
 
 void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) {
     mDisplayIds = displayIds;
 }
 
-void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) {
+void PowerAdvisor::setTotalFrameTargetWorkDuration(Duration targetDuration) {
     mTotalFrameTargetDuration = targetDuration;
 }
 
 std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds(
-        std::optional<nsecs_t> DisplayTimingData::*sortBy) {
+        std::optional<TimePoint> DisplayTimingData::*sortBy) {
     std::vector<DisplayId> sortedDisplays;
     std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays),
                  [&](DisplayId id) {
@@ -360,33 +356,34 @@
     return sortedDisplays;
 }
 
-std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
+std::optional<Duration> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
     if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) {
         return std::nullopt;
     }
 
     // Tracks when we finish presenting to hwc
-    nsecs_t estimatedEndTime = mCommitStartTimes[0];
+    TimePoint estimatedEndTime = mCommitStartTimes[0];
 
     // How long we spent this frame not doing anything, waiting for fences or vsync
-    nsecs_t idleDuration = 0;
+    Duration idleDuration = 0ns;
 
     // Most recent previous gpu end time in the current frame, probably from a prior display, used
     // as the start time for the next gpu operation if it ran over time since it probably blocked
-    std::optional<nsecs_t> previousValidGpuEndTime;
+    std::optional<TimePoint> previousValidGpuEndTime;
 
     // The currently estimated gpu end time for the frame,
     // used to accumulate gpu time as we iterate over the active displays
-    std::optional<nsecs_t> estimatedGpuEndTime;
+    std::optional<TimePoint> estimatedGpuEndTime;
 
     // If we're predicting at the start of the frame, we use last frame as our reference point
     // If we're predicting at the end of the frame, we use the current frame as a reference point
-    nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]);
+    TimePoint referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]);
 
     // When the prior frame should be presenting to the display
     // If we're predicting at the start of the frame, we use last frame's expected present time
     // If we're predicting at the end of the frame, the present fence time is already known
-    nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime);
+    TimePoint lastFramePresentTime =
+            (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime);
 
     // The timing info for the previously calculated display, if there was one
     std::optional<DisplayTimeline> previousDisplayReferenceTiming;
@@ -427,10 +424,10 @@
         // Track how long we spent waiting for the fence, can be excluded from the timing estimate
         idleDuration += estimatedTiming.probablyWaitsForPresentFence
                 ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime
-                : 0;
+                : 0ns;
 
         // Track how long we spent waiting to present, can be excluded from the timing estimate
-        idleDuration += earlyHint ? 0 : referenceTiming.hwcPresentDelayDuration;
+        idleDuration += earlyHint ? 0ns : referenceTiming.hwcPresentDelayDuration;
 
         // Estimate the reference frame's gpu timing
         auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime);
@@ -438,15 +435,15 @@
             previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration;
 
             // Estimate the prediction frame's gpu end time from the reference frame
-            estimatedGpuEndTime =
-                    std::max(estimatedTiming.hwcPresentStartTime, estimatedGpuEndTime.value_or(0)) +
+            estimatedGpuEndTime = std::max(estimatedTiming.hwcPresentStartTime,
+                                           estimatedGpuEndTime.value_or(TimePoint{0ns})) +
                     gpuTiming->duration;
         }
         previousDisplayReferenceTiming = referenceTiming;
     }
-    ATRACE_INT64("Idle duration", idleDuration);
+    ATRACE_INT64("Idle duration", idleDuration.ns());
 
-    nsecs_t estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime;
+    TimePoint estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime;
 
     // Don't count time spent idly waiting in the estimate as we could do more work in that time
     estimatedEndTime -= idleDuration;
@@ -454,21 +451,22 @@
 
     // We finish the frame when both present and the gpu are done, so wait for the later of the two
     // Also add the frame delay duration since the target did not move while we were delayed
-    nsecs_t totalDuration = mFrameDelayDuration +
-            std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0];
+    Duration totalDuration = mFrameDelayDuration +
+            std::max(estimatedEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) -
+            mCommitStartTimes[0];
 
     // We finish SurfaceFlinger when post-composition finishes, so add that in here
-    nsecs_t flingerDuration =
+    Duration flingerDuration =
             estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
 
     // Combine the two timings into a single normalized one
-    nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
+    Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
 
     return std::make_optional(combinedDuration);
 }
 
-nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) {
-    nsecs_t targetDuration;
+Duration PowerAdvisor::combineTimingEstimates(Duration totalDuration, Duration flingerDuration) {
+    Duration targetDuration{0ns};
     {
         std::lock_guard lock(mPowerHalMutex);
         targetDuration = *getPowerHal()->getTargetWorkDuration();
@@ -477,17 +475,18 @@
 
     // Normalize total to the flinger target (vsync period) since that's how often we actually send
     // hints
-    nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration;
+    Duration normalizedTotalDuration = Duration::fromNs((targetDuration.ns() * totalDuration.ns()) /
+                                                        mTotalFrameTargetDuration->ns());
     return std::max(flingerDuration, normalizedTotalDuration);
 }
 
 PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference(
-        nsecs_t fenceTime, nsecs_t displayStartTime) {
+        TimePoint fenceTime, TimePoint displayStartTime) {
     DisplayTimeline estimated;
     estimated.hwcPresentStartTime = displayStartTime;
 
     // We don't predict waiting for vsync alignment yet
-    estimated.hwcPresentDelayDuration = 0;
+    estimated.hwcPresentDelayDuration = 0ns;
 
     // How long we expect to run before we start waiting for the fence
     // For now just re-use last frame's post-present duration and assume it will not change much
@@ -502,12 +501,11 @@
 }
 
 PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline(
-        nsecs_t fenceTime) {
+        TimePoint fenceTime) {
     DisplayTimeline timeline;
     // How long between calling hwc present and trying to wait on the fence
-    const nsecs_t fenceWaitStartDelay =
-            (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated)
-                    .count();
+    const Duration fenceWaitStartDelay =
+            (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated);
 
     // Did our reference frame wait for an appropriate vsync before calling into hwc
     const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() &&
@@ -522,7 +520,7 @@
 
     // How long hwc present was delayed waiting for the next appropriate vsync
     timeline.hwcPresentDelayDuration =
-            (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0);
+            (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0ns);
     // When we started waiting for the present fence after calling into hwc present
     timeline.presentFenceWaitStartTime =
             timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay;
@@ -537,23 +535,26 @@
 }
 
 std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming(
-        std::optional<nsecs_t> previousEnd) {
+        std::optional<TimePoint> previousEndTime) {
     if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) {
         return std::nullopt;
     }
-    const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime);
-    const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime();
-    nsecs_t gpuDuration = 0;
-    if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID &&
-        latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) {
+    const TimePoint latestGpuStartTime =
+            std::max(previousEndTime.value_or(TimePoint{0ns}), *gpuStartTime);
+    const nsecs_t gpuEndFenceSignal = gpuEndFenceTime->getSignalTime();
+    Duration gpuDuration{0ns};
+    if (gpuEndFenceSignal != Fence::SIGNAL_TIME_INVALID &&
+        gpuEndFenceSignal != Fence::SIGNAL_TIME_PENDING) {
+        const TimePoint latestGpuEndTime = TimePoint::fromNs(gpuEndFenceSignal);
+
         // If we know how long the most recent gpu duration was, use that
         gpuDuration = latestGpuEndTime - latestGpuStartTime;
     } else if (lastValidGpuEndTime.has_value()) {
         // If we don't have the fence data, use the most recent information we do have
         gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime;
-        if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) {
+        if (gpuEndFenceSignal == Fence::SIGNAL_TIME_PENDING) {
             // If pending but went over the previous duration, use current time as the end
-            gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime);
+            gpuDuration = std::max(gpuDuration, Duration{TimePoint::now() - latestGpuStartTime});
         }
     }
     return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime};
@@ -614,15 +615,15 @@
 
     bool startPowerHintSession() override { return false; }
 
-    void setTargetWorkDuration(int64_t) override {}
+    void setTargetWorkDuration(Duration) override {}
 
-    void sendActualWorkDuration(int64_t, nsecs_t) override {}
+    void sendActualWorkDuration(Duration, TimePoint) 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; }
+    std::optional<Duration> getTargetWorkDuration() override { return std::nullopt; }
 
 private:
     const sp<V1_3::IPower> mPowerHal = nullptr;
@@ -640,8 +641,6 @@
     }
 
     mSupportsPowerHint = checkPowerHintSessionSupported();
-
-    mAllowedActualDeviation = base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", 0);
 }
 
 AidlPowerHalWrapper::~AidlPowerHalWrapper() {
@@ -729,9 +728,9 @@
         ALOGV("Cannot start power hint session, skipping");
         return false;
     }
-    auto ret =
-            mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
-                                         mPowerHintThreadIds, mTargetDuration, &mPowerHintSession);
+    auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
+                                            mPowerHintThreadIds, mTargetDuration.ns(),
+                                            &mPowerHintSession);
     if (!ret.isOk()) {
         ALOGW("Failed to start power hint session with error: %s",
               ret.exceptionToString(ret.exceptionCode()).c_str());
@@ -741,14 +740,14 @@
     return isPowerHintSessionRunning();
 }
 
-void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) {
+void AidlPowerHalWrapper::setTargetWorkDuration(Duration targetDuration) {
     ATRACE_CALL();
     mTargetDuration = targetDuration;
-    if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration);
+    if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
     if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
-        ALOGV("Sending target time: %" PRId64 "ns", targetDuration);
+        ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
         mLastTargetDurationSent = targetDuration;
-        auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration);
+        auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration.ns());
         if (!ret.isOk()) {
             ALOGW("Failed to set power hint target work duration with error: %s",
                   ret.exceptionMessage().c_str());
@@ -757,65 +756,40 @@
     }
 }
 
-bool AidlPowerHalWrapper::shouldReportActualDurations() {
-    // Report if we have never reported before or will go stale next frame
-    if (!mLastActualDurationSent.has_value() ||
-        (mLastTargetDurationSent + systemTime() - mLastActualReportTimestamp) >
-                kStaleTimeout.count()) {
-        return true;
-    }
-
-    if (!mActualDuration.has_value()) {
-        return false;
-    }
-    // Report if the change in actual duration exceeds the threshold
-    return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation;
-}
-
-void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) {
+void AidlPowerHalWrapper::sendActualWorkDuration(Duration actualDuration, TimePoint timestamp) {
     ATRACE_CALL();
-
-    if (actualDuration < 0 || !isPowerHintSessionRunning()) {
+    if (actualDuration < 0ns || !isPowerHintSessionRunning()) {
         ALOGV("Failed to send actual work duration, skipping");
         return;
     }
-    const nsecs_t reportedDuration = actualDuration;
-
-    mActualDuration = reportedDuration;
+    mActualDuration = actualDuration;
     WorkDuration duration;
-    duration.durationNanos = reportedDuration;
-    duration.timeStampNanos = timestamp;
+    duration.durationNanos = actualDuration.ns();
+    duration.timeStampNanos = timestamp.ns();
     mPowerHintQueue.push_back(duration);
 
     if (sTraceHintSessionData) {
-        ATRACE_INT64("Measured duration", actualDuration);
-        ATRACE_INT64("Target error term", actualDuration - mTargetDuration);
+        ATRACE_INT64("Measured duration", actualDuration.ns());
+        ATRACE_INT64("Target error term", Duration{actualDuration - mTargetDuration}.ns());
 
-        ATRACE_INT64("Reported duration", reportedDuration);
-        ATRACE_INT64("Reported target", mLastTargetDurationSent);
-        ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent);
+        ATRACE_INT64("Reported duration", actualDuration.ns());
+        ATRACE_INT64("Reported target", mLastTargetDurationSent.ns());
+        ATRACE_INT64("Reported target error term",
+                     Duration{actualDuration - mLastTargetDurationSent}.ns());
     }
 
     ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64
           " with error: %" PRId64,
-          reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent);
+          actualDuration.ns(), mLastTargetDurationSent.ns(),
+          Duration{actualDuration - mLastTargetDurationSent}.ns());
 
-    // 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 (shouldReportActualDurations()) {
-        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 actual duration here for rate limiting
-        mLastActualDurationSent = actualDuration;
+    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();
 }
 
 bool AidlPowerHalWrapper::shouldReconnectHAL() {
@@ -826,80 +800,73 @@
     return mPowerHintThreadIds;
 }
 
-std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() {
+std::optional<Duration> AidlPowerHalWrapper::getTargetWorkDuration() {
     return mTargetDuration;
 }
 
-void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) {
-    mAllowedActualDeviation = allowedDeviation;
-}
-
 const bool AidlPowerHalWrapper::sTraceHintSessionData =
         base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
 
 PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
-    static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
-    static bool sHasHal = true;
-
-    if (!sHasHal) {
+    if (!mHasHal) {
         return nullptr;
     }
 
     // Grab old hint session values before we destroy any existing wrapper
     std::vector<int32_t> oldPowerHintSessionThreadIds;
-    std::optional<int64_t> oldTargetWorkDuration;
+    std::optional<Duration> oldTargetWorkDuration;
 
-    if (sHalWrapper != nullptr) {
-        oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
-        oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
+    if (mHalWrapper != nullptr) {
+        oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds();
+        oldTargetWorkDuration = mHalWrapper->getTargetWorkDuration();
     }
 
     // If we used to have a HAL, but it stopped responding, attempt to reconnect
     if (mReconnectPowerHal) {
-        sHalWrapper = nullptr;
+        mHalWrapper = nullptr;
         mReconnectPowerHal = false;
     }
 
-    if (sHalWrapper != nullptr) {
-        auto wrapper = sHalWrapper.get();
+    if (mHalWrapper != nullptr) {
+        auto wrapper = mHalWrapper.get();
         // If the wrapper is fine, return it, but if it indicates a reconnect, remake it
         if (!wrapper->shouldReconnectHAL()) {
             return wrapper;
         }
         ALOGD("Reconnecting Power HAL");
-        sHalWrapper = nullptr;
+        mHalWrapper = 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();
+    mHalWrapper = AidlPowerHalWrapper::connect();
 
     // If that didn't succeed, attempt to connect to the HIDL Power HAL
-    if (sHalWrapper == nullptr) {
-        sHalWrapper = HidlPowerHalWrapper::connect();
+    if (mHalWrapper == nullptr) {
+        mHalWrapper = HidlPowerHalWrapper::connect();
     } else {
         ALOGD("Successfully connecting AIDL Power HAL");
         // If AIDL, pass on any existing hint session values
-        sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+        mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
         // Only set duration and start if duration is defined
         if (oldTargetWorkDuration.has_value()) {
-            sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+            mHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
             // Only start if possible to run and both threadids and duration are defined
             if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
-                mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
+                mPowerHintSessionRunning = mHalWrapper->startPowerHintSession();
             }
         }
     }
 
     // If we make it to this point and still don't have a HAL, it's unlikely we
     // will, so stop trying
-    if (sHalWrapper == nullptr) {
-        sHasHal = false;
+    if (mHalWrapper == nullptr) {
+        mHasHal = false;
     }
 
-    return sHalWrapper.get();
+    return mHalWrapper.get();
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 859cf3d..1c9d123 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -27,6 +27,7 @@
 
 #include <android/hardware/power/IPower.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <scheduler/Time.h>
 #include <ui/DisplayIdentification.h>
 #include "../Scheduler/OneShotTimer.h"
 
@@ -53,7 +54,7 @@
     virtual bool supportsPowerHintSession() = 0;
     virtual bool isPowerHintSessionRunning() = 0;
     // Sends a power hint that updates to the target work duration for the frame
-    virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
+    virtual void setTargetWorkDuration(Duration targetDuration) = 0;
     // Sends a power hint for the actual known work duration at the end of the frame
     virtual void sendActualWorkDuration() = 0;
     // Sends a power hint for the upcoming frame predicted from previous frame timing
@@ -65,33 +66,33 @@
     // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
     virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
     // Reports the start and end times of a hwc validate call this frame for a given display
-    virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
-                                      nsecs_t validateEndTime) = 0;
+    virtual void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
+                                      TimePoint validateEndTime) = 0;
     // Reports the start and end times of a hwc present call this frame for a given display
-    virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
-                                     nsecs_t presentEndTime) = 0;
+    virtual void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
+                                     TimePoint presentEndTime) = 0;
     // Reports the expected time that the current frame will present to the display
-    virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0;
+    virtual void setExpectedPresentTime(TimePoint expectedPresentTime) = 0;
     // Reports the most recent present fence time and end time once known
-    virtual void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) = 0;
+    virtual void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) = 0;
     // Reports whether a display used client composition this frame
     virtual void setRequiresClientComposition(DisplayId displayId,
                                               bool requiresClientComposition) = 0;
     // Reports whether a given display skipped validation this frame
     virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0;
     // Reports when a hwc present is delayed, and the time that it will resume
-    virtual void setHwcPresentDelayedTime(
-            DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0;
+    virtual void setHwcPresentDelayedTime(DisplayId displayId,
+                                          TimePoint earliestFrameStartTime) = 0;
     // Reports the start delay for SurfaceFlinger this frame
-    virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0;
+    virtual void setFrameDelay(Duration frameDelayDuration) = 0;
     // Reports the SurfaceFlinger commit start time this frame
-    virtual void setCommitStart(nsecs_t commitStartTime) = 0;
+    virtual void setCommitStart(TimePoint commitStartTime) = 0;
     // Reports the SurfaceFlinger composite end time this frame
-    virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0;
+    virtual void setCompositeEnd(TimePoint compositeEndTime) = 0;
     // Reports the list of the currently active displays
     virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
     // Sets the target duration for the entire pipeline including the gpu
-    virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0;
+    virtual void setTotalFrameTargetWorkDuration(Duration targetDuration) = 0;
 };
 
 namespace impl {
@@ -111,11 +112,11 @@
         virtual void restartPowerHintSession() = 0;
         virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
         virtual bool startPowerHintSession() = 0;
-        virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
-        virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0;
+        virtual void setTargetWorkDuration(Duration targetDuration) = 0;
+        virtual void sendActualWorkDuration(Duration actualDuration, TimePoint timestamp) = 0;
         virtual bool shouldReconnectHAL() = 0;
         virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0;
-        virtual std::optional<nsecs_t> getTargetWorkDuration() = 0;
+        virtual std::optional<Duration> getTargetWorkDuration() = 0;
     };
 
     PowerAdvisor(SurfaceFlinger& flinger);
@@ -129,31 +130,36 @@
     bool usePowerHintSession() override;
     bool supportsPowerHintSession() override;
     bool isPowerHintSessionRunning() override;
-    void setTargetWorkDuration(nsecs_t targetDuration) override;
+    void setTargetWorkDuration(Duration targetDuration) override;
     void sendActualWorkDuration() override;
     void sendPredictedWorkDuration() override;
     void enablePowerHint(bool enabled) override;
     bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
     void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime);
-    void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime,
-                              nsecs_t validateEndTime) override;
-    void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
-                             nsecs_t presentEndTime) override;
+    void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
+                              TimePoint validateEndTime) override;
+    void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
+                             TimePoint presentEndTime) override;
     void setSkippedValidate(DisplayId displayId, bool skipped) override;
     void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override;
-    void setExpectedPresentTime(nsecs_t expectedPresentTime) override;
-    void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override;
-    void setHwcPresentDelayedTime(
-            DisplayId displayId,
-            std::chrono::steady_clock::time_point earliestFrameStartTime) override;
+    void setExpectedPresentTime(TimePoint expectedPresentTime) override;
+    void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) override;
+    void setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) override;
 
-    void setFrameDelay(nsecs_t frameDelayDuration) override;
-    void setCommitStart(nsecs_t commitStartTime) override;
-    void setCompositeEnd(nsecs_t compositeEndTime) override;
+    void setFrameDelay(Duration frameDelayDuration) override;
+    void setCommitStart(TimePoint commitStartTime) override;
+    void setCompositeEnd(TimePoint compositeEndTime) override;
     void setDisplays(std::vector<DisplayId>& displayIds) override;
-    void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override;
+    void setTotalFrameTargetWorkDuration(Duration targetDuration) override;
 
 private:
+    friend class PowerAdvisorTest;
+
+    // Tracks if powerhal exists
+    bool mHasHal = true;
+    // Holds the hal wrapper for getPowerHal
+    std::unique_ptr<HalWrapper> mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr;
+
     HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
     bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
     std::mutex mPowerHalMutex;
@@ -171,44 +177,45 @@
     // Higher-level timing data used for estimation
     struct DisplayTimeline {
         // The start of hwc present, or the start of validate if it happened there instead
-        nsecs_t hwcPresentStartTime = -1;
+        TimePoint hwcPresentStartTime;
         // The end of hwc present or validate, whichever one actually presented
-        nsecs_t hwcPresentEndTime = -1;
+        TimePoint hwcPresentEndTime;
         // How long the actual hwc present was delayed after hwcPresentStartTime
-        nsecs_t hwcPresentDelayDuration = 0;
+        Duration hwcPresentDelayDuration{0ns};
         // When we think we started waiting for the present fence after calling into hwc present and
         // after potentially waiting for the earliest present time
-        nsecs_t presentFenceWaitStartTime = -1;
+        TimePoint presentFenceWaitStartTime;
         // How long we ran after we finished waiting for the fence but before hwc present finished
-        nsecs_t postPresentFenceHwcPresentDuration = 0;
+        Duration postPresentFenceHwcPresentDuration{0ns};
         // Are we likely to have waited for the present fence during composition
         bool probablyWaitsForPresentFence = false;
         // Estimate one frame's timeline from that of a previous frame
-        DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime);
+        DisplayTimeline estimateTimelineFromReference(TimePoint fenceTime,
+                                                      TimePoint displayStartTime);
     };
 
     struct GpuTimeline {
-        nsecs_t duration = 0;
-        nsecs_t startTime = -1;
+        Duration duration{0ns};
+        TimePoint startTime;
     };
 
     // Power hint session data recorded from the pipeline
     struct DisplayTimingData {
         std::unique_ptr<FenceTime> gpuEndFenceTime;
-        std::optional<nsecs_t> gpuStartTime;
-        std::optional<nsecs_t> lastValidGpuEndTime;
-        std::optional<nsecs_t> lastValidGpuStartTime;
-        std::optional<nsecs_t> hwcPresentStartTime;
-        std::optional<nsecs_t> hwcPresentEndTime;
-        std::optional<nsecs_t> hwcValidateStartTime;
-        std::optional<nsecs_t> hwcValidateEndTime;
-        std::optional<nsecs_t> hwcPresentDelayedTime;
+        std::optional<TimePoint> gpuStartTime;
+        std::optional<TimePoint> lastValidGpuEndTime;
+        std::optional<TimePoint> lastValidGpuStartTime;
+        std::optional<TimePoint> hwcPresentStartTime;
+        std::optional<TimePoint> hwcPresentEndTime;
+        std::optional<TimePoint> hwcValidateStartTime;
+        std::optional<TimePoint> hwcValidateEndTime;
+        std::optional<TimePoint> hwcPresentDelayedTime;
         bool usedClientComposition = false;
         bool skippedValidate = false;
         // Calculate high-level timing milestones from more granular display timing data
-        DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime);
+        DisplayTimeline calculateDisplayTimeline(TimePoint fenceTime);
         // Estimate the gpu duration for a given display from previous gpu timing data
-        std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd);
+        std::optional<GpuTimeline> estimateGpuTiming(std::optional<TimePoint> previousEndTime);
     };
 
     template <class T, size_t N>
@@ -233,30 +240,31 @@
     };
 
     // Filter and sort the display ids by a given property
-    std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy);
+    std::vector<DisplayId> getOrderedDisplayIds(
+            std::optional<TimePoint> DisplayTimingData::*sortBy);
     // Estimates a frame's total work duration including gpu time.
     // Runs either at the beginning or end of a frame, using the most recent data available
-    std::optional<nsecs_t> estimateWorkDuration(bool earlyHint);
+    std::optional<Duration> estimateWorkDuration(bool earlyHint);
     // There are two different targets and actual work durations we care about,
     // this normalizes them together and takes the max of the two
-    nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration);
+    Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration);
 
     std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
 
     // Current frame's delay
-    nsecs_t mFrameDelayDuration = 0;
+    Duration mFrameDelayDuration{0ns};
     // Last frame's post-composition duration
-    nsecs_t mLastPostcompDuration = 0;
+    Duration mLastPostcompDuration{0ns};
     // Buffer of recent commit start times
-    RingBuffer<nsecs_t, 2> mCommitStartTimes;
+    RingBuffer<TimePoint, 2> mCommitStartTimes;
     // Buffer of recent expected present times
-    RingBuffer<nsecs_t, 2> mExpectedPresentTimes;
-    // Most recent present fence time, set at the end of the frame once known
-    nsecs_t mLastPresentFenceTime = -1;
-    // Most recent present fence time, set at the end of the frame once known
-    nsecs_t mLastSfPresentEndTime = -1;
-    // Target for the entire pipeline including gpu
-    std::optional<nsecs_t> mTotalFrameTargetDuration;
+    RingBuffer<TimePoint, 2> mExpectedPresentTimes;
+    // Most recent present fence time, provided by SF after composition engine finishes presenting
+    TimePoint mLastPresentFenceTime;
+    // Most recent composition engine present end time, returned with the present fence from SF
+    TimePoint mLastSfPresentEndTime;
+    // Target duration for the entire pipeline including gpu
+    std::optional<Duration> mTotalFrameTargetDuration;
     // Updated list of display IDs
     std::vector<DisplayId> mDisplayIds;
 
@@ -266,11 +274,11 @@
 
     // An adjustable safety margin which pads the "actual" value sent to PowerHAL,
     // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error
-    static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms;
+    static constexpr const Duration kTargetSafetyMargin{1ms};
 
     // How long we expect hwc to run after the present call until it waits for the fence
-    static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us;
-    static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us;
+    static constexpr const Duration kFenceWaitStartDelayValidated{150us};
+    static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us};
 };
 
 class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
@@ -287,21 +295,17 @@
     void restartPowerHintSession() override;
     void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
     bool startPowerHintSession() override;
-    void setTargetWorkDuration(nsecs_t targetDuration) override;
-    void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override;
+    void setTargetWorkDuration(Duration targetDuration) override;
+    void sendActualWorkDuration(Duration actualDuration, TimePoint timestamp) override;
     bool shouldReconnectHAL() override;
     std::vector<int32_t> getPowerHintSessionThreadIds() override;
-    std::optional<nsecs_t> getTargetWorkDuration() override;
+    std::optional<Duration> getTargetWorkDuration() override;
 
 private:
     friend class AidlPowerHalWrapperTest;
 
     bool checkPowerHintSessionSupported();
     void closePowerHintSession();
-    bool shouldReportActualDurations();
-
-    // Used for testing
-    void setAllowedActualDeviation(nsecs_t);
 
     const sp<hardware::power::IPower> mPowerHal = nullptr;
     bool mHasExpensiveRendering = false;
@@ -316,24 +320,15 @@
     // Queue of actual durations saved to report
     std::vector<hardware::power::WorkDuration> mPowerHintQueue;
     // The latest values we have received for target and actual
-    nsecs_t mTargetDuration = kDefaultTarget.count();
-    std::optional<nsecs_t> mActualDuration;
+    Duration mTargetDuration = kDefaultTargetDuration;
+    std::optional<Duration> 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 = false;
-    // Keep track of the last messages sent for rate limiter change detection
-    std::optional<nsecs_t> mLastActualDurationSent;
-    // Timestamp of the last report we sent, used to avoid stale sessions
-    nsecs_t mLastActualReportTimestamp = 0;
-    nsecs_t mLastTargetDurationSent = kDefaultTarget.count();
-    // Max amount the error term can vary without causing an actual value report
-    nsecs_t mAllowedActualDeviation = -1;
+    Duration mLastTargetDurationSent = kDefaultTargetDuration;
     // Whether we should emit ATRACE_INT data for hint sessions
     static const bool sTraceHintSessionData;
-    static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms;
-    // Amount of time after the last message was sent before the session goes stale
-    // actually 100ms but we use 80 here to give some slack
-    static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
+    static constexpr Duration kDefaultTargetDuration{16ms};
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
deleted file mode 100644
index d8bbc30..0000000
--- a/services/surfaceflinger/EffectLayer.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2007 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"
-
-// #define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "EffectLayer"
-
-#include "EffectLayer.h"
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include "DisplayDevice.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-EffectLayer::EffectLayer(const LayerCreationArgs& args)
-      : Layer(args),
-        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {}
-
-EffectLayer::~EffectLayer() = default;
-
-std::vector<compositionengine::LayerFE::LayerSettings> EffectLayer::prepareClientCompositionList(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
-    std::vector<compositionengine::LayerFE::LayerSettings> results;
-    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
-            prepareClientComposition(targetSettings);
-    // Nothing to render.
-    if (!layerSettings) {
-        return {};
-    }
-
-    // set the shadow for the layer if needed
-    prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
-
-    // If fill bounds are occluded or the fill color is invalid skip the fill settings.
-    if (targetSettings.realContentIsVisible && fillsColor()) {
-        // Set color for color fill settings.
-        layerSettings->source.solidColor = getColor().rgb;
-        results.push_back(*layerSettings);
-    } else if (hasBlur() || drawShadows()) {
-        layerSettings->skipContentDraw = true;
-        results.push_back(*layerSettings);
-    }
-
-    return results;
-}
-
-bool EffectLayer::isVisible() const {
-    return hasSomethingToDraw() && !isHiddenByPolicy() && (getAlpha() > 0.0_hf || hasBlur());
-}
-
-bool EffectLayer::setColor(const half3& color) {
-    if (mDrawingState.color.r == color.r && mDrawingState.color.g == color.g &&
-        mDrawingState.color.b == color.b) {
-        return false;
-    }
-
-    mDrawingState.sequence++;
-    mDrawingState.color.r = color.r;
-    mDrawingState.color.g = color.g;
-    mDrawingState.color.b = color.b;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool EffectLayer::setDataspace(ui::Dataspace dataspace) {
-    if (mDrawingState.dataspace == dataspace) {
-        return false;
-    }
-
-    mDrawingState.sequence++;
-    mDrawingState.dataspace = dataspace;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-void EffectLayer::preparePerFrameCompositionState() {
-    Layer::preparePerFrameCompositionState();
-
-    auto* compositionState = editCompositionState();
-    compositionState->color = getColor();
-    compositionState->compositionType =
-            aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
-}
-
-sp<compositionengine::LayerFE> EffectLayer::getCompositionEngineLayerFE() const {
-    // There's no need to get a CE Layer if the EffectLayer isn't going to draw anything. In that
-    // case, it acts more like a ContainerLayer so returning a null CE Layer makes more sense
-    if (hasSomethingToDraw()) {
-        return asLayerFE();
-    } else {
-        return nullptr;
-    }
-}
-
-compositionengine::LayerFECompositionState* EffectLayer::editCompositionState() {
-    return mCompositionState.get();
-}
-
-const compositionengine::LayerFECompositionState* EffectLayer::getCompositionState() const {
-    return mCompositionState.get();
-}
-
-bool EffectLayer::isOpaque(const Layer::State& s) const {
-    // Consider the layer to be opaque if its opaque flag is set or its effective
-    // alpha (considering the alpha of its parents as well) is 1.0;
-    return (s.flags & layer_state_t::eLayerOpaque) != 0 || (fillsColor() && getAlpha() == 1.0_hf);
-}
-
-ui::Dataspace EffectLayer::getDataSpace() const {
-    return mDrawingState.dataspace;
-}
-
-sp<Layer> EffectLayer::createClone() {
-    sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer(
-            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
-    layer->setInitialValuesForClone(sp<Layer>::fromExisting(this));
-    return layer;
-}
-
-bool EffectLayer::fillsColor() const {
-    return mDrawingState.color.r >= 0.0_hf && mDrawingState.color.g >= 0.0_hf &&
-            mDrawingState.color.b >= 0.0_hf;
-}
-
-bool EffectLayer::hasBlur() const {
-    return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EffectLayer.h b/services/surfaceflinger/EffectLayer.h
deleted file mode 100644
index 1dcb633..0000000
--- a/services/surfaceflinger/EffectLayer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2007 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 <sys/types.h>
-
-#include <cstdint>
-
-#include "Layer.h"
-
-namespace android {
-
-// A layer that can render a combination of the following effects.
-//   * fill the bounds of the layer with a color
-//   * render a shadow cast by the bounds of the layer
-// If no effects are enabled, the layer is considered to be invisible.
-class EffectLayer : public Layer {
-public:
-    explicit EffectLayer(const LayerCreationArgs&);
-    ~EffectLayer() override;
-
-    sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
-    compositionengine::LayerFECompositionState* editCompositionState() override;
-
-    const char* getType() const override { return "EffectLayer"; }
-    bool isVisible() const override;
-
-    bool setColor(const half3& color) override;
-
-    bool setDataspace(ui::Dataspace dataspace) override;
-
-    ui::Dataspace getDataSpace() const override;
-
-    bool isOpaque(const Layer::State& s) const override;
-
-protected:
-    /*
-     * compositionengine::LayerFE overrides
-     */
-    const compositionengine::LayerFECompositionState* getCompositionState() const override;
-    void preparePerFrameCompositionState() override;
-    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
-            compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) override;
-
-    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
-
-    sp<Layer> createClone() override;
-
-private:
-    // Returns true if there is a valid color to fill.
-    bool fillsColor() const;
-    // Returns true if this layer has a blur value.
-    bool hasBlur() const;
-    bool hasSomethingToDraw() const { return fillsColor() || drawShadows() || hasBlur(); }
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index c73a75c..cd1ba70 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -892,6 +892,10 @@
         mJankType = JankType::Unknown;
         deadlineDelta = 0;
         deltaToVsync = 0;
+        if (mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+            mSurfaceFlingerActuals.presentTime = mSurfaceFlingerActuals.endTime;
+        }
+
         return;
     }
 
@@ -1168,22 +1172,50 @@
             static_cast<float>(totalPresentToPresentWalls);
 }
 
+std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const {
+    for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
+        const auto& [fence, _] = mPendingPresentFences[i];
+        if (fence && fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+            return i;
+        }
+    }
+
+    return {};
+}
+
 void FrameTimeline::flushPendingPresentFences() {
+    const auto firstSignaledFence = getFirstSignalFenceIndex();
+    if (!firstSignaledFence.has_value()) {
+        return;
+    }
+
     // Perfetto is using boottime clock to void drifts when the device goes
     // to suspend.
     const auto monoBootOffset = mUseBootTimeClock
             ? (systemTime(SYSTEM_TIME_BOOTTIME) - systemTime(SYSTEM_TIME_MONOTONIC))
             : 0;
 
+    // Present fences are expected to be signaled in order. Mark all the previous
+    // pending fences as errors.
+    for (size_t i = 0; i < firstSignaledFence.value(); i++) {
+        const auto& pendingPresentFence = *mPendingPresentFences.begin();
+        const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
+        auto& displayFrame = pendingPresentFence.second;
+        displayFrame->onPresent(signalTime, mPreviousPresentTime);
+        displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
+        mPendingPresentFences.erase(mPendingPresentFences.begin());
+    }
+
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& pendingPresentFence = mPendingPresentFences[i];
         nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
         if (pendingPresentFence.first && pendingPresentFence.first->isValid()) {
             signalTime = pendingPresentFence.first->getSignalTime();
             if (signalTime == Fence::SIGNAL_TIME_PENDING) {
-                continue;
+                break;
             }
         }
+
         auto& displayFrame = pendingPresentFence.second;
         displayFrame->onPresent(signalTime, mPreviousPresentTime);
         displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index a2305af..31074b1 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -474,6 +474,7 @@
     friend class android::frametimeline::FrameTimelineTest;
 
     void flushPendingPresentFences() REQUIRES(mMutex);
+    std::optional<size_t> getFirstSignalFenceIndex() const REQUIRES(mMutex);
     void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
     void dumpAll(std::string& result);
     void dumpJank(std::string& result);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5e1a858..9bb9305 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -29,6 +29,7 @@
 #include <android-base/stringprintf.h>
 #include <android/native_window.h>
 #include <binder/IPCThreadState.h>
+#include <compositionengine/CompositionEngine.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
@@ -39,8 +40,10 @@
 #include <ftl/enum.h>
 #include <ftl/fake_guard.h>
 #include <gui/BufferItem.h>
+#include <gui/GLConsumer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
+#include <gui/TraceUtils.h>
 #include <math.h>
 #include <private/android_filesystem_config.h>
 #include <renderengine/RenderEngine.h>
@@ -62,10 +65,8 @@
 #include <mutex>
 #include <sstream>
 
-#include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWComposer.h"
-#include "EffectLayer.h"
 #include "FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "LayerProtoHelper.h"
@@ -74,11 +75,72 @@
 #include "TunnelModeEnabledReporter.h"
 
 #define DEBUG_RESIZE 0
+#define EARLY_RELEASE_ENABLED false
 
 namespace android {
 namespace {
 constexpr int kDumpTableRowLength = 159;
+
+static constexpr float defaultMaxLuminance = 1000.0;
+
 const ui::Transform kIdentityTransform;
+
+constexpr mat4 inverseOrientation(uint32_t transform) {
+    const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+    const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+    const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+    mat4 tr;
+
+    if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+        tr = tr * rot90;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+        tr = tr * flipH;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+        tr = tr * flipV;
+    }
+    return inverse(tr);
+}
+
+bool assignTransform(ui::Transform* dst, ui::Transform& from) {
+    if (*dst == from) {
+        return false;
+    }
+    *dst = from;
+    return true;
+}
+
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+    using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+    using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+    const auto frameRateCompatibility = [frameRate] {
+        switch (frameRate.type) {
+            case Layer::FrameRateCompatibility::Default:
+                return FrameRateCompatibility::Default;
+            case Layer::FrameRateCompatibility::ExactOrMultiple:
+                return FrameRateCompatibility::ExactOrMultiple;
+            default:
+                return FrameRateCompatibility::Undefined;
+        }
+    }();
+
+    const auto seamlessness = [frameRate] {
+        switch (frameRate.seamlessness) {
+            case scheduler::Seamlessness::OnlySeamless:
+                return Seamlessness::ShouldBeSeamless;
+            case scheduler::Seamlessness::SeamedAndSeamless:
+                return Seamlessness::NotRequired;
+            default:
+                return Seamlessness::Undefined;
+        }
+    }();
+
+    return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+                                       .frameRateCompatibility = frameRateCompatibility,
+                                       .seamlessness = seamlessness};
+}
+
 } // namespace
 
 using namespace ftl::flag_operators;
@@ -100,7 +162,11 @@
         mWindowType(static_cast<WindowInfo::Type>(
                 args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
         mLayerCreationFlags(args.flags),
-        mBorderEnabled(false) {
+        mBorderEnabled(false),
+        mTextureName(args.textureName),
+        mHwcSlotGenerator(sp<HwcSlotGenerator>::make()) {
+    ALOGV("Creating Layer %s", getDebugName());
+
     uint32_t layerFlags = 0;
     if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
     if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -111,16 +177,11 @@
         sSequence = *args.sequence + 1;
     }
     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 = ui::DEFAULT_LAYER_STACK;
     mDrawingState.sequence = 0;
-    mDrawingState.requested_legacy = mDrawingState.active_legacy;
-    mDrawingState.width = UINT32_MAX;
-    mDrawingState.height = UINT32_MAX;
     mDrawingState.transform.set(0, 0);
     mDrawingState.frameNumber = 0;
     mDrawingState.bufferTransform = 0;
@@ -129,6 +190,7 @@
     mDrawingState.acquireFence = sp<Fence>::make(-1);
     mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
     mDrawingState.dataspace = ui::Dataspace::UNKNOWN;
+    mDrawingState.dataspaceRequested = false;
     mDrawingState.hdrMetadata.validTypes = 0;
     mDrawingState.surfaceDamageRegion = Region::INVALID_REGION;
     mDrawingState.cornerRadius = 0.0f;
@@ -170,6 +232,17 @@
         mOwnerUid = mCallingUid;
         mOwnerPid = mCallingPid;
     }
+
+    mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
+    mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+    mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
+    mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
+
+    mSnapshot->sequence = sequence;
+    mSnapshot->name = getDebugName();
+    mSnapshot->textureName = mTextureName;
+    mSnapshot->premultipliedAlpha = mPremultipliedAlpha;
+    mSnapshot->transform = {};
 }
 
 void Layer::onFirstRef() {
@@ -177,6 +250,28 @@
 }
 
 Layer::~Layer() {
+    // The original layer and the clone layer share the same texture and buffer. Therefore, only
+    // one of the layers, in this case the original layer, needs to handle the deletion. The
+    // original layer and the clone should be removed at the same time so there shouldn't be any
+    // issue with the clone layer trying to use the texture.
+    if (mBufferInfo.mBuffer != nullptr) {
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                  mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
+                                  mBufferInfo.mFence,
+                                  mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
+                                          mOwnerUid));
+    }
+    if (!isClone()) {
+        // The original layer and the clone layer share the same texture. Therefore, only one of
+        // the layers, in this case the original layer, needs to handle the deletion. The original
+        // layer and the clone should be removed at the same time so there shouldn't be any issue
+        // with the clone layer trying to use the deleted texture.
+        mFlinger->deleteTextureAsync(mTextureName);
+    }
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->onDestroy(layerId);
+    mFlinger->mFrameTracer->onDestroy(layerId);
+
     sp<Client> c(mClientRef.promote());
     if (c != 0) {
         c->detachLayer(this);
@@ -209,13 +304,6 @@
 // callbacks
 // ---------------------------------------------------------------------------
 
-/*
- * onLayerDisplayed is only meaningful for BufferLayer, but, is called through
- * Layer.  So, the implementation is done in BufferLayer.  When called on a
- * EffectLayer object, it's essentially a NOP.
- */
-void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult>) {}
-
 void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
     if (mDrawingState.zOrderRelativeOf == nullptr) {
         return;
@@ -381,6 +469,10 @@
     for (const sp<Layer>& child : mDrawingChildren) {
         child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
     }
+
+    if (mPotentialCursor) {
+        prepareCursorCompositionState();
+    }
 }
 
 Rect Layer::getCroppedBufferSize(const State& s) const {
@@ -416,40 +508,40 @@
                                         : Hwc2::IComposerClient::BlendMode::COVERAGE;
     }
 
-    auto* compositionState = editCompositionState();
-    compositionState->outputFilter = getOutputFilter();
-    compositionState->isVisible = isVisible();
-    compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
-    compositionState->shadowRadius = mEffectiveShadowRadius;
+    auto* snapshot = editLayerSnapshot();
+    snapshot->outputFilter = getOutputFilter();
+    snapshot->isVisible = isVisible();
+    snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+    snapshot->shadowRadius = mEffectiveShadowRadius;
 
-    compositionState->contentDirty = contentDirty;
+    snapshot->contentDirty = contentDirty;
     contentDirty = false;
 
-    compositionState->geomLayerBounds = mBounds;
-    compositionState->geomLayerTransform = getTransform();
-    compositionState->geomInverseLayerTransform = compositionState->geomLayerTransform.inverse();
-    compositionState->transparentRegionHint = getActiveTransparentRegion(drawingState);
+    snapshot->geomLayerBounds = mBounds;
+    snapshot->geomLayerTransform = getTransform();
+    snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse();
+    snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState);
 
-    compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
-    compositionState->alpha = alpha;
-    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
-    compositionState->blurRegions = drawingState.blurRegions;
-    compositionState->stretchEffect = getStretchEffect();
+    snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+    snapshot->alpha = alpha;
+    snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    snapshot->blurRegions = drawingState.blurRegions;
+    snapshot->stretchEffect = getStretchEffect();
 }
 
 void Layer::prepareGeometryCompositionState() {
     const auto& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    auto* snapshot = editLayerSnapshot();
 
-    compositionState->geomBufferSize = getBufferSize(drawingState);
-    compositionState->geomContentCrop = getBufferCrop();
-    compositionState->geomCrop = getCrop(drawingState);
-    compositionState->geomBufferTransform = getBufferTransform();
-    compositionState->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
-    compositionState->geomUsesSourceCrop = usesSourceCrop();
-    compositionState->isSecure = isSecure();
+    snapshot->geomBufferSize = getBufferSize(drawingState);
+    snapshot->geomContentCrop = getBufferCrop();
+    snapshot->geomCrop = getCrop(drawingState);
+    snapshot->geomBufferTransform = getBufferTransform();
+    snapshot->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
+    snapshot->geomUsesSourceCrop = usesSourceCrop();
+    snapshot->isSecure = isSecure();
 
-    compositionState->metadata.clear();
+    snapshot->metadata.clear();
     const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
     for (const auto& [key, mandatory] : supportedMetadata) {
         const auto& genericLayerMetadataCompatibilityMap =
@@ -465,50 +557,90 @@
             continue;
         }
 
-        compositionState->metadata
-                .emplace(key, compositionengine::GenericLayerMetadataEntry{mandatory, it->second});
+        snapshot->metadata.emplace(key,
+                                   compositionengine::GenericLayerMetadataEntry{mandatory,
+                                                                                it->second});
     }
 }
 
 void Layer::preparePerFrameCompositionState() {
     const auto& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    auto* snapshot = editLayerSnapshot();
 
-    compositionState->forceClientComposition = false;
+    snapshot->forceClientComposition = false;
 
-    compositionState->isColorspaceAgnostic = isColorSpaceAgnostic();
-    compositionState->dataspace = getDataSpace();
-    compositionState->colorTransform = getColorTransform();
-    compositionState->colorTransformIsIdentity = !hasColorTransform();
-    compositionState->surfaceDamage = surfaceDamageRegion;
-    compositionState->hasProtectedContent = isProtected();
-    compositionState->dimmingEnabled = isDimmingEnabled();
+    snapshot->isColorspaceAgnostic = isColorSpaceAgnostic();
+    snapshot->dataspace = getDataSpace();
+    snapshot->colorTransform = getColorTransform();
+    snapshot->colorTransformIsIdentity = !hasColorTransform();
+    snapshot->surfaceDamage = surfaceDamageRegion;
+    snapshot->hasProtectedContent = isProtected();
+    snapshot->dimmingEnabled = isDimmingEnabled();
 
     const bool usesRoundedCorners = hasRoundedCorners();
 
-    compositionState->isOpaque =
-            isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
+    snapshot->isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
     // Rounded corners no longer force client composition, since we may use a
     // hole punch so that the layer will appear to have rounded corners.
     if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 ||
-        compositionState->stretchEffect.hasEffect()) {
-        compositionState->forceClientComposition = true;
+        snapshot->stretchEffect.hasEffect()) {
+        snapshot->forceClientComposition = true;
     }
     // If there are no visible region changes, we still need to update blur parameters.
-    compositionState->blurRegions = drawingState.blurRegions;
-    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    snapshot->blurRegions = drawingState.blurRegions;
+    snapshot->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());
+    snapshot->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
+
+    if (hasBufferOrSidebandStream()) {
+        preparePerFrameBufferCompositionState();
+    } else {
+        preparePerFrameEffectsCompositionState();
+    }
+}
+
+void Layer::preparePerFrameBufferCompositionState() {
+    // Sideband layers
+    auto* snapshot = editLayerSnapshot();
+    if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) {
+        snapshot->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
+        return;
+    } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
+        snapshot->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+    } else {
+        // Normal buffer layers
+        snapshot->hdrMetadata = mBufferInfo.mHdrMetadata;
+        snapshot->compositionType = mPotentialCursor
+                ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
+                : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
+    }
+
+    snapshot->buffer = getBuffer();
+    snapshot->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
+            ? 0
+            : mBufferInfo.mBufferSlot;
+    snapshot->acquireFence = mBufferInfo.mFence;
+    snapshot->frameNumber = mBufferInfo.mFrameNumber;
+    snapshot->sidebandStreamHasFrame = false;
+}
+
+void Layer::preparePerFrameEffectsCompositionState() {
+    auto* snapshot = editLayerSnapshot();
+    snapshot->color = getColor();
+    snapshot->compositionType =
+            aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
 }
 
 void Layer::prepareCursorCompositionState() {
     const State& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    auto* snapshot = editLayerSnapshot();
 
     // Apply the layer's transform, followed by the display's global transform
     // Here we're guaranteed that the layer's transform preserves rects
@@ -517,7 +649,7 @@
     Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
     Rect frame(getTransform().transform(bounds));
 
-    compositionState->cursorFrame = frame;
+    snapshot->cursorFrame = frame;
 }
 
 sp<compositionengine::LayerFE> Layer::asLayerFE() const {
@@ -526,46 +658,6 @@
     return sp<compositionengine::LayerFE>::fromExisting(layerFE);
 }
 
-sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
-    return nullptr;
-}
-
-compositionengine::LayerFECompositionState* Layer::editCompositionState() {
-    return nullptr;
-}
-
-const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
-    return nullptr;
-}
-
-bool Layer::onPreComposition(nsecs_t) {
-    return false;
-}
-
-void Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) {
-    using StateSubset = compositionengine::LayerFE::StateSubset;
-
-    switch (subset) {
-        case StateSubset::BasicGeometry:
-            prepareBasicGeometryCompositionState();
-            break;
-
-        case StateSubset::GeometryAndContent:
-            prepareBasicGeometryCompositionState();
-            prepareGeometryCompositionState();
-            preparePerFrameCompositionState();
-            break;
-
-        case StateSubset::Content:
-            preparePerFrameCompositionState();
-            break;
-
-        case StateSubset::Cursor:
-            prepareCursorCompositionState();
-            break;
-    }
-}
-
 const char* Layer::getDebugName() const {
     return mName.c_str();
 }
@@ -575,32 +667,54 @@
 // ---------------------------------------------------------------------------
 
 std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
-    if (!getCompositionState()) {
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+            prepareClientCompositionInternal(targetSettings);
+    // Nothing to render.
+    if (!layerSettings) {
         return {};
     }
 
-    FloatRect bounds = getBounds();
-    half alpha = getAlpha();
+    // HWC requests to clear this layer.
+    if (targetSettings.clearContent) {
+        prepareClearClientComposition(*layerSettings, false /* blackout */);
+        return layerSettings;
+    }
+
+    // set the shadow for the layer if needed
+    prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
+
+    return layerSettings;
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionInternal(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+    ATRACE_CALL();
+
+    const auto* snapshot = getLayerSnapshot();
+    if (!snapshot) {
+        return {};
+    }
 
     compositionengine::LayerFE::LayerSettings layerSettings;
-    layerSettings.geometry.boundaries = bounds;
-    layerSettings.geometry.positionTransform = getTransform().asMatrix4();
+    layerSettings.geometry.boundaries =
+            reduce(snapshot->geomLayerBounds, snapshot->transparentRegionHint);
+    layerSettings.geometry.positionTransform = snapshot->geomLayerTransform.asMatrix4();
 
     // skip drawing content if the targetSettings indicate the content will be occluded
     const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent;
     layerSettings.skipContentDraw = !drawContent;
 
     if (hasColorTransform()) {
-        layerSettings.colorTransform = getColorTransform();
+        layerSettings.colorTransform = snapshot->colorTransform;
     }
 
-    const auto roundedCornerState = getRoundedCornerState();
+    const auto& roundedCornerState = snapshot->roundedCorner;
     layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
     layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
 
-    layerSettings.alpha = alpha;
-    layerSettings.sourceDataspace = getDataSpace();
+    layerSettings.alpha = snapshot->alpha;
+    layerSettings.sourceDataspace = snapshot->dataspace;
 
     // Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
     // We do this here instead of in buffer info so that dumpsys can still report layers that are
@@ -617,26 +731,31 @@
     layerSettings.whitePointNits = targetSettings.whitePointNits;
     switch (targetSettings.blurSetting) {
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
-            layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
-            layerSettings.blurRegions = getBlurRegions();
-            layerSettings.blurRegionTransform =
-                    getActiveTransform(getDrawingState()).inverse().asMatrix4();
+            layerSettings.backgroundBlurRadius = snapshot->backgroundBlurRadius;
+            layerSettings.blurRegions = snapshot->blurRegions;
+            layerSettings.blurRegionTransform = snapshot->geomInverseLayerTransform.asMatrix4();
             break;
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
-            layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+            layerSettings.backgroundBlurRadius = snapshot->backgroundBlurRadius;
             break;
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
-            layerSettings.blurRegions = getBlurRegions();
-            layerSettings.blurRegionTransform =
-                    getActiveTransform(getDrawingState()).inverse().asMatrix4();
+            layerSettings.blurRegions = snapshot->blurRegions;
+            layerSettings.blurRegionTransform = snapshot->geomInverseLayerTransform.asMatrix4();
             break;
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
         default:
             break;
     }
-    layerSettings.stretchEffect = getStretchEffect();
+    layerSettings.stretchEffect = snapshot->stretchEffect;
     // Record the name of the layer for debugging further down the stack.
-    layerSettings.name = getName();
+    layerSettings.name = snapshot->name;
+
+    if (hasEffect() && !hasBufferOrSidebandStream()) {
+        prepareEffectsClientComposition(layerSettings, targetSettings);
+        return layerSettings;
+    }
+
+    prepareBufferStateClientComposition(layerSettings, targetSettings);
     return layerSettings;
 }
 
@@ -650,31 +769,132 @@
 
     // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
     layerSettings.alpha = blackout ? 1.0f : 0.0f;
-    layerSettings.name = getName();
+    layerSettings.name = getLayerSnapshot()->name;
 }
 
-// TODO(b/188891810): This method now only ever returns 0 or 1 layers so we should return
-// std::optional instead of a vector.  Additionally, we should consider removing
-// this method entirely in favor of calling prepareClientComposition directly.
-std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
-    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
-            prepareClientComposition(targetSettings);
-    // Nothing to render.
-    if (!layerSettings) {
-        return {};
+void Layer::prepareEffectsClientComposition(
+        compositionengine::LayerFE::LayerSettings& layerSettings,
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+    // If fill bounds are occluded or the fill color is invalid skip the fill settings.
+    if (targetSettings.realContentIsVisible && fillsColor()) {
+        // Set color for color fill settings.
+        layerSettings.source.solidColor = getColor().rgb;
+    } else if (hasBlur() || drawShadows()) {
+        layerSettings.skipContentDraw = true;
+    }
+}
+
+void Layer::prepareBufferStateClientComposition(
+        compositionengine::LayerFE::LayerSettings& layerSettings,
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+    ATRACE_CALL();
+    const auto* snapshot = getLayerSnapshot();
+    if (CC_UNLIKELY(!snapshot->externalTexture)) {
+        // If there is no buffer for the layer or we have sidebandstream where there is no
+        // activeBuffer, then we need to return LayerSettings.
+        return;
+    }
+    const bool blackOutLayer =
+            (snapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) ||
+            ((snapshot->isSecure || snapshot->hasProtectedContent) && !targetSettings.isSecure);
+    const bool bufferCanBeUsedAsHwTexture =
+            snapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+    if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+        ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+                 snapshot->name.c_str());
+        prepareClearClientComposition(layerSettings, true /* blackout */);
+        return;
     }
 
-    // HWC requests to clear this layer.
-    if (targetSettings.clearContent) {
-        prepareClearClientComposition(*layerSettings, false /* blackout */);
-        return {*layerSettings};
+    layerSettings.source.buffer.buffer = snapshot->externalTexture;
+    layerSettings.source.buffer.isOpaque = snapshot->contentOpaque;
+    layerSettings.source.buffer.fence = snapshot->acquireFence;
+    layerSettings.source.buffer.textureName = snapshot->textureName;
+    layerSettings.source.buffer.usePremultipliedAlpha = snapshot->premultipliedAlpha;
+    layerSettings.source.buffer.isY410BT2020 = snapshot->isHdrY410;
+    bool hasSmpte2086 = snapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+    bool hasCta861_3 = snapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
+    float maxLuminance = 0.f;
+    if (hasSmpte2086 && hasCta861_3) {
+        maxLuminance = std::min(snapshot->hdrMetadata.smpte2086.maxLuminance,
+                                snapshot->hdrMetadata.cta8613.maxContentLightLevel);
+    } else if (hasSmpte2086) {
+        maxLuminance = snapshot->hdrMetadata.smpte2086.maxLuminance;
+    } else if (hasCta861_3) {
+        maxLuminance = snapshot->hdrMetadata.cta8613.maxContentLightLevel;
+    } else {
+        switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+            case HAL_DATASPACE_TRANSFER_ST2084:
+            case HAL_DATASPACE_TRANSFER_HLG:
+                // Behavior-match previous releases for HDR content
+                maxLuminance = defaultMaxLuminance;
+                break;
+        }
+    }
+    layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
+    layerSettings.frameNumber = snapshot->frameNumber;
+    layerSettings.bufferId = snapshot->externalTexture->getId();
+
+    const bool useFiltering = targetSettings.needsFiltering ||
+            snapshot->geomLayerTransform.needsBilinearFiltering() || snapshot->bufferNeedsFiltering;
+
+    // Query the texture matrix given our current filtering mode.
+    float textureMatrix[16];
+    getDrawingTransformMatrix(useFiltering, textureMatrix);
+
+    if (snapshot->geomBufferUsesDisplayInverseTransform) {
+        /*
+         * the code below applies the primary display's inverse transform to
+         * the texture transform
+         */
+        uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        mat4 tr = inverseOrientation(transform);
+
+        /**
+         * TODO(b/36727915): This is basically a hack.
+         *
+         * Ensure that regardless of the parent transformation,
+         * this buffer is always transformed from native display
+         * orientation to display orientation. For example, in the case
+         * of a camera where the buffer remains in native orientation,
+         * we want the pixels to always be upright.
+         */
+        const auto parentTransform = snapshot->transform;
+        tr = tr * inverseOrientation(parentTransform.getOrientation());
+
+        // and finally apply it to the original texture matrix
+        const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
+        memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
     }
 
-    // set the shadow for the layer if needed
-    prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
+    const Rect win{layerSettings.geometry.boundaries};
+    float bufferWidth = snapshot->bufferSize.getWidth();
+    float bufferHeight = snapshot->bufferSize.getHeight();
 
-    return {*layerSettings};
+    // Layers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+    // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+    // ignore them.
+    if (!snapshot->bufferSize.isValid()) {
+        bufferWidth = float(win.right) - float(win.left);
+        bufferHeight = float(win.bottom) - float(win.top);
+    }
+
+    const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+    const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+    const float translateY = float(win.top) / bufferHeight;
+    const float translateX = float(win.left) / bufferWidth;
+
+    // Flip y-coordinates because GLConsumer expects OpenGL convention.
+    mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
+            mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
+            mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
+            mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
+
+    layerSettings.source.buffer.useTextureFiltering = useFiltering;
+    layerSettings.source.buffer.textureTransform =
+            mat4(static_cast<const float*>(textureMatrix)) * tr;
+
+    return;
 }
 
 aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
@@ -765,16 +985,6 @@
     mTransactionFlags |= mask;
 }
 
-bool Layer::setPosition(float x, float y) {
-    if (mDrawingState.transform.tx() == x && mDrawingState.transform.ty() == y) return false;
-    mDrawingState.sequence++;
-    mDrawingState.transform.set(x, y);
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
 bool Layer::setChildLayer(const sp<Layer>& childLayer, int32_t z) {
     ssize_t idx = mCurrentChildren.indexOf(childLayer);
     if (idx < 0) {
@@ -899,20 +1109,6 @@
     return (p != nullptr) && p->isTrustedOverlay();
 }
 
-bool Layer::setSize(uint32_t w, uint32_t h) {
-    if (mDrawingState.requested_legacy.w == w && mDrawingState.requested_legacy.h == h)
-        return false;
-    mDrawingState.requested_legacy.w = w;
-    mDrawingState.requested_legacy.h = h;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    // record the new size, from this point on, when the client request
-    // a buffer, it'll get the new size.
-    setDefaultBufferSize(mDrawingState.requested_legacy.w, mDrawingState.requested_legacy.h);
-    return true;
-}
-
 bool Layer::setAlpha(float alpha) {
     if (mDrawingState.color.a == alpha) return false;
     mDrawingState.sequence++;
@@ -982,27 +1178,10 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
-bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
-    if (matrix.dsdx == mDrawingState.transform.dsdx() &&
-        matrix.dtdy == mDrawingState.transform.dtdy() &&
-        matrix.dtdx == mDrawingState.transform.dtdx() &&
-        matrix.dsdy == mDrawingState.transform.dsdy()) {
-        return false;
-    }
-
-    ui::Transform t;
-    t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-
-    mDrawingState.sequence++;
-    mDrawingState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-    mDrawingState.modified = true;
-
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
 
 bool Layer::setTransparentRegionHint(const Region& transparent) {
-    mDrawingState.requestedTransparentRegion_legacy = transparent;
+    mDrawingState.sequence++;
+    mDrawingState.transparentRegionHint = transparent;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1031,9 +1210,8 @@
 }
 
 bool Layer::setCrop(const Rect& crop) {
-    if (mDrawingState.requestedCrop == crop) return false;
+    if (mDrawingState.crop == crop) return false;
     mDrawingState.sequence++;
-    mDrawingState.requestedCrop = crop;
     mDrawingState.crop = crop;
 
     mDrawingState.modified = true;
@@ -1457,7 +1635,6 @@
     sp<Layer> parent = mDrawingParent.promote();
     info.mParentName = parent ? parent->getName() : "none"s;
     info.mType = getType();
-    info.mTransparentRegion = ds.activeTransparentRegion_legacy;
 
     info.mVisibleRegion = getVisibleRegion(display);
     info.mSurfaceDamageRegion = surfaceDamageRegion;
@@ -1465,8 +1642,6 @@
     info.mX = ds.transform.tx();
     info.mY = ds.transform.ty();
     info.mZ = ds.z;
-    info.mWidth = ds.width;
-    info.mHeight = ds.height;
     info.mCrop = ds.crop;
     info.mColor = ds.color;
     info.mFlags = ds.flags;
@@ -2006,36 +2181,18 @@
 }
 
 void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
-                                           const Rect& layerStackRect) {
-    renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
-
-    // Note: this preserves existing behavior of shadowing the entire layer and not cropping it if
-    // transparent regions are present. This may not be necessary since shadows are only cast by
-    // SurfaceFlinger's EffectLayers, which do not typically use transparent regions.
-    state.boundaries = mBounds;
+                                           const Rect& layerStackRect) const {
+    const auto* snapshot = getLayerSnapshot();
+    renderengine::ShadowSettings state = snapshot->shadowSettings;
+    if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
+        return;
+    }
 
     // Shift the spot light x-position to the middle of the display and then
     // offset it by casting layer's screen pos.
-    state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left;
-    state.lightPos.y -= mScreenBounds.top;
-
-    state.length = mEffectiveShadowRadius;
-
-    if (state.length > 0.f) {
-        const float casterAlpha = caster.alpha;
-        const bool casterIsOpaque =
-                ((caster.source.buffer.buffer != nullptr) && caster.source.buffer.isOpaque);
-
-        // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
-        // Otherwise the generated shadow will only be shown around the casting layer.
-        state.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
-        state.ambientColor *= casterAlpha;
-        state.spotColor *= casterAlpha;
-
-        if (state.ambientColor.a > 0.f && state.spotColor.a > 0.f) {
-            caster.shadow = state;
-        }
-    }
+    state.lightPos.x = (layerStackRect.width() / 2.f) - snapshot->transformedBounds.left;
+    state.lightPos.y -= snapshot->transformedBounds.top;
+    caster.shadow = state;
 }
 
 bool Layer::findInHierarchy(const sp<Layer>& l) {
@@ -2165,7 +2322,7 @@
         }
     }
 
-    LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
+    LayerProtoHelper::writeToProto(state.transparentRegionHint,
                                    [&]() { return layerInfo->mutable_transparent_region(); });
 
     layerInfo->set_layer_stack(getLayerStack().id);
@@ -2175,9 +2332,6 @@
         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));
@@ -2237,14 +2391,6 @@
     return mRemovedFromDrawingState;
 }
 
-ui::Transform Layer::getInputTransform() const {
-    return getTransform();
-}
-
-Rect Layer::getInputBounds() const {
-    return getCroppedBufferSize(getDrawingState());
-}
-
 // Applies the given transform to the region, while protecting against overflows caused by any
 // offsets. If applying the offset in the transform to any of the Rects in the region would result
 // in an overflow, they are not added to the output Region.
@@ -2508,10 +2654,6 @@
             mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
 }
 
-bool Layer::canReceiveInput() const {
-    return !isHiddenByPolicy();
-}
-
 compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
         const DisplayDevice* display) const {
     if (!display) return nullptr;
@@ -2526,6 +2668,40 @@
 void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
     cloneDrawingState(clonedFrom.get());
     mClonedFrom = clonedFrom;
+    mPremultipliedAlpha = clonedFrom->mPremultipliedAlpha;
+    mPotentialCursor = clonedFrom->mPotentialCursor;
+    mProtectedByApp = clonedFrom->mProtectedByApp;
+    updateCloneBufferInfo();
+}
+
+void Layer::updateCloneBufferInfo() {
+    if (!isClone() || !isClonedFromAlive()) {
+        return;
+    }
+
+    sp<Layer> clonedFrom = getClonedFrom();
+    mBufferInfo = clonedFrom->mBufferInfo;
+    mSidebandStream = clonedFrom->mSidebandStream;
+    surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
+    mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
+    mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
+
+    // After buffer info is updated, the drawingState from the real layer needs to be copied into
+    // the cloned. This is because some properties of drawingState can change when latchBuffer is
+    // called. However, copying the drawingState would also overwrite the cloned layer's relatives
+    // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
+    // the cloned drawingState again.
+    wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
+    SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+    wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
+    WindowInfo tmpInputInfo = mDrawingState.inputInfo;
+
+    cloneDrawingState(clonedFrom.get());
+
+    mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
+    mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
+    mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+    mDrawingState.inputInfo = tmpInputInfo;
 }
 
 void Layer::updateMirrorInfo() {
@@ -2733,18 +2909,1372 @@
     mDrawingState.callbackHandles = {};
 }
 
-bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) {
-    if (handles.empty()) {
+void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+                                      const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+                                      const sp<Fence>& releaseFence,
+                                      uint32_t currentMaxAcquiredBufferCount) {
+    if (!listener) {
+        return;
+    }
+    ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
+    listener->onReleaseBuffer({buffer->getId(), framenumber},
+                              releaseFence ? releaseFence : Fence::NO_FENCE,
+                              currentMaxAcquiredBufferCount);
+}
+
+void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
+    // If we are displayed on multiple displays in a single composition cycle then we would
+    // need to do careful tracking to enable the use of the mLastClientCompositionFence.
+    //  For example we can only use it if all the displays are client comp, and we need
+    //  to merge all the client comp fences. We could do this, but for now we just
+    // disable the optimization when a layer is composed on multiple displays.
+    if (mClearClientCompositionFenceOnLayerDisplayed) {
+        mLastClientCompositionFence = nullptr;
+    } else {
+        mClearClientCompositionFenceOnLayerDisplayed = 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
+    // let the client know when that previous buffer is removed from the screen.
+    //
+    // Every other transaction on this layer does not need a release fence because no other
+    // Transactions that were set on this layer this frame are going to have their preceding buffer
+    // removed from the display this frame.
+    //
+    // For example, if we have 3 transactions this frame. The first transaction doesn't contain a
+    // buffer so it doesn't need a previous release fence because the layer still needs the previous
+    // buffer. The second transaction contains a buffer so it needs a previous release fence because
+    // the previous buffer will be released this frame. The third transaction also contains a
+    // buffer. It replaces the buffer in the second transaction. The buffer in the second
+    // transaction will now no longer be presented so it is released immediately and the third
+    // transaction doesn't need a previous release fence.
+    sp<CallbackHandle> ch;
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer &&
+            mDrawingState.releaseBufferEndpoint == handle->listener) {
+            ch = handle;
+            break;
+        }
+    }
+
+    // Prevent tracing the same release multiple times.
+    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
+        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
+    }
+
+    if (ch != nullptr) {
+        ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+        ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
+        ch->name = mName;
+    }
+}
+
+void Layer::onSurfaceFrameCreated(
+        const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+    while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
+        // Too many SurfaceFrames pending classification. The front of the deque is probably not
+        // tracked by FrameTimeline and will never be presented. This will only result in a memory
+        // leak.
+        ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
+              mName.c_str());
+        std::string miniDump = mPendingJankClassifications.front()->miniDump();
+        ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
+        mPendingJankClassifications.pop_front();
+    }
+    mPendingJankClassifications.emplace_back(surfaceFrame);
+}
+
+void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->transformHint = mTransformHint;
+        handle->dequeueReadyTime = dequeueReadyTime;
+        handle->currentMaxAcquiredBufferCount =
+                mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
+        ATRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(),
+                              handle->previousReleaseCallbackId.framenumber);
+    }
+
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer &&
+            mDrawingState.releaseBufferEndpoint == handle->listener) {
+            handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+            break;
+        }
+    }
+
+    std::vector<JankData> jankData;
+    jankData.reserve(mPendingJankClassifications.size());
+    while (!mPendingJankClassifications.empty() &&
+           mPendingJankClassifications.front()->getJankType()) {
+        std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
+                mPendingJankClassifications.front();
+        mPendingJankClassifications.pop_front();
+        jankData.emplace_back(
+                JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
+    }
+
+    mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles,
+                                                                 jankData);
+    mDrawingState.callbackHandles = {};
+}
+
+bool Layer::willPresentCurrentTransaction() const {
+    // Returns true if the most recent Transaction applied to CurrentState will be presented.
+    return (getSidebandStreamChanged() || getAutoRefresh() ||
+            (mDrawingState.modified &&
+             (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr)));
+}
+
+bool Layer::setTransform(uint32_t transform) {
+    if (mDrawingState.bufferTransform == transform) return false;
+    mDrawingState.bufferTransform = transform;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) {
+    if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false;
+    mDrawingState.sequence++;
+    mDrawingState.transformToDisplayInverse = transformToDisplayInverse;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setBufferCrop(const Rect& bufferCrop) {
+    if (mDrawingState.bufferCrop == bufferCrop) return false;
+
+    mDrawingState.sequence++;
+    mDrawingState.bufferCrop = bufferCrop;
+
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setDestinationFrame(const Rect& destinationFrame) {
+    if (mDrawingState.destinationFrame == destinationFrame) return false;
+
+    mDrawingState.sequence++;
+    mDrawingState.destinationFrame = destinationFrame;
+
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+// Translate destination frame into scale and position. If a destination frame is not set, use the
+// provided scale and position
+bool Layer::updateGeometry() {
+    if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
+        mDrawingState.destinationFrame.isEmpty()) {
+        // If destination frame is not set, use the requested transform set via
+        // Layer::setPosition and Layer::setMatrix.
+        return assignTransform(&mDrawingState.transform, mRequestedTransform);
+    }
+
+    Rect destRect = mDrawingState.destinationFrame;
+    int32_t destW = destRect.width();
+    int32_t destH = destRect.height();
+    if (destRect.left < 0) {
+        destRect.left = 0;
+        destRect.right = destW;
+    }
+    if (destRect.top < 0) {
+        destRect.top = 0;
+        destRect.bottom = destH;
+    }
+
+    if (!mDrawingState.buffer) {
+        ui::Transform t;
+        t.set(destRect.left, destRect.top);
+        return assignTransform(&mDrawingState.transform, t);
+    }
+
+    uint32_t bufferWidth = mDrawingState.buffer->getWidth();
+    uint32_t bufferHeight = mDrawingState.buffer->getHeight();
+    // Undo any transformations on the buffer.
+    if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+    uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+    if (mDrawingState.transformToDisplayInverse) {
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+
+    float sx = destW / static_cast<float>(bufferWidth);
+    float sy = destH / static_cast<float>(bufferHeight);
+    ui::Transform t;
+    t.set(sx, 0, 0, sy);
+    t.set(destRect.left, destRect.top);
+    return assignTransform(&mDrawingState.transform, t);
+}
+
+bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
+    if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
+        mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
         return false;
     }
 
+    mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+
+    mDrawingState.sequence++;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+
+    return true;
+}
+
+bool Layer::setPosition(float x, float y) {
+    if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
+        return false;
+    }
+
+    mRequestedTransform.set(x, y);
+
+    mDrawingState.sequence++;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+
+    return true;
+}
+
+bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                      const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
+                      bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
+                      const FrameTimelineInfo& info) {
+    ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
+    if (!buffer) {
+        return false;
+    }
+
+    const bool frameNumberChanged =
+            bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
+    const uint64_t frameNumber =
+            frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1;
+    ATRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber);
+
+    if (mDrawingState.buffer) {
+        mReleasePreviousBuffer = true;
+        if (!mBufferInfo.mBuffer ||
+            (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
+             mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) {
+            // If mDrawingState has a buffer, and we are about to update again
+            // before swapping to drawing state, then the first buffer will be
+            // dropped and we should decrement the pending buffer count and
+            // call any release buffer callbacks if set.
+            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                      mDrawingState.acquireFence,
+                                      mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
+                                              mOwnerUid));
+            decrementPendingBufferCount();
+            if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
+                mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
+                addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
+                mDrawingState.bufferSurfaceFrameTX.reset();
+            }
+        } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
+            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                      mLastClientCompositionFence,
+                                      mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
+                                              mOwnerUid));
+            mLastClientCompositionFence = nullptr;
+        }
+    }
+
+    mDrawingState.frameNumber = frameNumber;
+    mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
+    mDrawingState.buffer = std::move(buffer);
+    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);
+    if (mDrawingState.acquireFenceTime->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+        // We latched this buffer unsiganled, so we need to pass the acquire fence
+        // on the callback instead of just the acquire time, since it's unknown at
+        // this point.
+        mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFence;
+    } else {
+        mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
+    }
+
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
+                                      mOwnerUid, postTime, getGameMode());
+    mDrawingState.desiredPresentTime = desiredPresentTime;
+    mDrawingState.isAutoTimestamp = isAutoTimestamp;
+
+    const nsecs_t presentTime = [&] {
+        if (!isAutoTimestamp) return desiredPresentTime;
+
+        const auto prediction =
+                mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
+        if (prediction.has_value()) return prediction->presentTime;
+
+        return static_cast<nsecs_t>(0);
+    }();
+
+    using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+    mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
+
+    setFrameTimelineVsyncForBufferTransaction(info, postTime);
+
+    if (dequeueTime && *dequeueTime != 0) {
+        const uint64_t bufferId = mDrawingState.buffer->getId();
+        mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
+                                               FrameTracer::FrameEvent::DEQUEUE);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
+                                               FrameTracer::FrameEvent::QUEUE);
+    }
+
+    mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
+    return true;
+}
+
+bool Layer::setDataspace(ui::Dataspace dataspace) {
+    mDrawingState.dataspaceRequested = true;
+    if (mDrawingState.dataspace == dataspace) return false;
+    mDrawingState.dataspace = dataspace;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
+    if (mDrawingState.hdrMetadata == hdrMetadata) return false;
+    mDrawingState.hdrMetadata = hdrMetadata;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+    mDrawingState.surfaceDamageRegion = surfaceDamage;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setApi(int32_t api) {
+    if (mDrawingState.api == api) return false;
+    mDrawingState.api = api;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
+    if (mDrawingState.sidebandStream == sidebandStream) return false;
+
+    if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
+        mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
+    } else if (sidebandStream != nullptr) {
+        mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount();
+    }
+
+    mDrawingState.sidebandStream = sidebandStream;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    if (!mSidebandStreamChanged.exchange(true)) {
+        // mSidebandStreamChanged was false
+        mFlinger->onLayerUpdate();
+    }
+    return true;
+}
+
+bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) {
+    // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
+    if (handles.empty()) {
+        mReleasePreviousBuffer = false;
+        return false;
+    }
+
+    const bool willPresent = willPresentCurrentTransaction();
+
     for (const auto& handle : handles) {
-        mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
+        // If this transaction set a buffer on this layer, release its previous buffer
+        handle->releasePreviousBuffer = mReleasePreviousBuffer;
+
+        // If this layer will be presented in this frame
+        if (willPresent) {
+            // If this transaction set an acquire fence on this layer, set its acquire time
+            handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
+            handle->frameNumber = mDrawingState.frameNumber;
+
+            // Store so latched time and release fence can be set
+            mDrawingState.callbackHandles.push_back(handle);
+
+        } else { // If this layer will NOT need to be relatched and presented this frame
+            // Notify the transaction completed thread this handle is done
+            mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
+        }
+    }
+
+    mReleasePreviousBuffer = false;
+    mCallbackHandleAcquireTimeOrFence = -1;
+
+    return willPresent;
+}
+
+Rect Layer::getBufferSize(const State& /*s*/) const {
+    // for buffer state layers we use the display frame size as the buffer size.
+
+    if (mBufferInfo.mBuffer == nullptr) {
+        return Rect::INVALID_RECT;
+    }
+
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+
+    // Undo any transformations on the buffer and return the result.
+    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (getTransformToDisplayInverse()) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
+        }
+    }
+
+    return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
+}
+
+FloatRect Layer::computeSourceBounds(const FloatRect& parentBounds) const {
+    if (mBufferInfo.mBuffer == nullptr) {
+        return parentBounds;
+    }
+
+    return getBufferSize(getDrawingState()).toFloatRect();
+}
+
+bool Layer::fenceHasSignaled() const {
+    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
+        return true;
+    }
+
+    const bool fenceSignaled =
+            getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+    if (!fenceSignaled) {
+        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+                                                    TimeStats::LatchSkipReason::LateAcquire);
+    }
+
+    return fenceSignaled;
+}
+
+bool Layer::onPreComposition(nsecs_t refreshStartTime, bool /* updatingOutputGeometryThisFrame */) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->refreshStartTime = refreshStartTime;
+    }
+    return hasReadyFrame();
+}
+
+void Layer::setAutoRefresh(bool autoRefresh) {
+    mDrawingState.autoRefresh = autoRefresh;
+}
+
+bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) {
+    // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+    auto* snapshot = editLayerSnapshot();
+    snapshot->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
+
+    if (mSidebandStreamChanged.exchange(false)) {
+        const State& s(getDrawingState());
+        // mSidebandStreamChanged was true
+        mSidebandStream = s.sidebandStream;
+        snapshot->sidebandStream = mSidebandStream;
+        if (mSidebandStream != nullptr) {
+            setTransactionFlags(eTransactionNeeded);
+            mFlinger->setTransactionFlags(eTraversalNeeded);
+        }
+        recomputeVisibleRegions = true;
+
+        return true;
+    }
+    return false;
+}
+
+bool Layer::hasFrameUpdate() const {
+    const State& c(getDrawingState());
+    return (mDrawingStateModified || mDrawingState.modified) &&
+            (c.buffer != nullptr || c.bgColorLayer != nullptr);
+}
+
+void Layer::updateTexImage(nsecs_t latchTime) {
+    const State& s(getDrawingState());
+
+    if (!s.buffer) {
+        if (s.bgColorLayer) {
+            for (auto& handle : mDrawingState.callbackHandles) {
+                handle->latchTime = latchTime;
+            }
+        }
+        return;
+    }
+
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->frameNumber == mDrawingState.frameNumber) {
+            handle->latchTime = latchTime;
+        }
+    }
+
+    const int32_t layerId = getSequence();
+    const uint64_t bufferId = mDrawingState.buffer->getId();
+    const uint64_t frameNumber = mDrawingState.frameNumber;
+    const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+    mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
+    mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime);
+
+    mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence,
+                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime,
+                                           FrameTracer::FrameEvent::LATCH);
+
+    auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+    if (bufferSurfaceFrame != nullptr &&
+        bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+        // Update only if the bufferSurfaceFrame wasn't already presented. A Presented
+        // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
+        // are processing the next state.
+        addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
+                                          mDrawingState.acquireFenceTime->getSignalTime(),
+                                          latchTime);
+        mDrawingState.bufferSurfaceFrameTX.reset();
+    }
+
+    std::deque<sp<CallbackHandle>> remainingHandles;
+    mFlinger->getTransactionCallbackInvoker()
+            .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+    mDrawingState.callbackHandles = remainingHandles;
+
+    mDrawingStateModified = false;
+}
+
+void Layer::gatherBufferInfo() {
+    if (!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
+        decrementPendingBufferCount();
+    }
+
+    mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
+    mBufferInfo.mBuffer = mDrawingState.buffer;
+    mBufferInfo.mFence = mDrawingState.acquireFence;
+    mBufferInfo.mFrameNumber = mDrawingState.frameNumber;
+    mBufferInfo.mPixelFormat =
+            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
+    mBufferInfo.mFrameLatencyNeeded = true;
+    mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime;
+    mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+    mBufferInfo.mFence = mDrawingState.acquireFence;
+    mBufferInfo.mTransform = mDrawingState.bufferTransform;
+    auto lastDataspace = mBufferInfo.mDataspace;
+    mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
+    if (lastDataspace != mBufferInfo.mDataspace) {
+        mFlinger->mSomeDataspaceChanged = true;
+    }
+    mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
+    mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+    mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion;
+    mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
+    mBufferInfo.mApi = mDrawingState.api;
+    mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
+    mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(mDrawingState.clientCacheId);
+}
+
+Rect Layer::computeBufferCrop(const State& s) {
+    if (s.buffer && !s.bufferCrop.isEmpty()) {
+        Rect bufferCrop;
+        s.buffer->getBounds().intersect(s.bufferCrop, &bufferCrop);
+        return bufferCrop;
+    } else if (s.buffer) {
+        return s.buffer->getBounds();
+    } else {
+        return s.bufferCrop;
+    }
+}
+
+sp<Layer> Layer::createClone() {
+    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
+    args.textureName = mTextureName;
+    sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
+    layer->mHwcSlotGenerator = mHwcSlotGenerator;
+    layer->setInitialValuesForClone(sp<Layer>::fromExisting(this));
+    return layer;
+}
+
+bool Layer::bufferNeedsFiltering() const {
+    const State& s(getDrawingState());
+    if (!s.buffer) {
+        return false;
+    }
+
+    int32_t bufferWidth = static_cast<int32_t>(s.buffer->getWidth());
+    int32_t bufferHeight = static_cast<int32_t>(s.buffer->getHeight());
+
+    // Undo any transformations on the buffer and return the result.
+    if (s.bufferTransform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+
+    if (s.transformToDisplayInverse) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+
+    const Rect layerSize{getBounds()};
+    int32_t layerWidth = layerSize.getWidth();
+    int32_t layerHeight = layerSize.getHeight();
+
+    // Align the layer orientation with the buffer before comparism
+    if (mTransformHint & ui::Transform::ROT_90) {
+        std::swap(layerWidth, layerHeight);
+    }
+
+    return layerWidth != bufferWidth || layerHeight != bufferHeight;
+}
+
+void Layer::decrementPendingBufferCount() {
+    int32_t pendingBuffers = --mPendingBufferTransactions;
+    tracePendingBufferCount(pendingBuffers);
+}
+
+void Layer::tracePendingBufferCount(int32_t pendingBuffers) {
+    ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
+}
+
+/*
+ * We don't want to send the layer's transform to input, but rather the
+ * parent's transform. This is because Layer's transform is
+ * information about how the buffer is placed on screen. The parent's
+ * transform makes more sense to send since it's information about how the
+ * layer is placed on screen. This transform is used by input to determine
+ * how to go from screen space back to window space.
+ */
+ui::Transform Layer::getInputTransform() const {
+    if (!hasBufferOrSidebandStream()) {
+        return getTransform();
+    }
+    sp<Layer> parent = mDrawingParent.promote();
+    if (parent == nullptr) {
+        return ui::Transform();
+    }
+
+    return parent->getTransform();
+}
+
+/**
+ * Similar to getInputTransform, we need to update the bounds to include the transform.
+ * This is because bounds don't include the buffer transform, where the input assumes
+ * that's already included.
+ */
+Rect Layer::getInputBounds() const {
+    if (!hasBufferOrSidebandStream()) {
+        return getCroppedBufferSize(getDrawingState());
+    }
+
+    Rect bufferBounds = getCroppedBufferSize(getDrawingState());
+    if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) {
+        return bufferBounds;
+    }
+    return mDrawingState.transform.transform(bufferBounds);
+}
+
+bool Layer::simpleBufferUpdate(const layer_state_t& s) const {
+    const uint64_t requiredFlags = layer_state_t::eBufferChanged;
+
+    const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
+            layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
+            layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
+            layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged |
+            layer_state_t::eReparent;
+
+    const uint64_t allowedFlags = layer_state_t::eHasListenerCallbacksChanged |
+            layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFrameRateChanged |
+            layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eApiChanged |
+            layer_state_t::eMetadataChanged | layer_state_t::eDropInputModeChanged |
+            layer_state_t::eInputInfoChanged;
+
+    if ((s.what & requiredFlags) != requiredFlags) {
+        ALOGV("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
+              (s.what | requiredFlags) & ~s.what);
+        return false;
+    }
+
+    if (s.what & deniedFlags) {
+        ALOGV("%s: false [has denied flags 0x%" PRIx64 "]", __func__, s.what & deniedFlags);
+        return false;
+    }
+
+    if (s.what & allowedFlags) {
+        ALOGV("%s: [has allowed flags 0x%" PRIx64 "]", __func__, s.what & allowedFlags);
+    }
+
+    if (s.what & layer_state_t::ePositionChanged) {
+        if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) {
+            ALOGV("%s: false [ePositionChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eAlphaChanged) {
+        if (mDrawingState.color.a != s.color.a) {
+            ALOGV("%s: false [eAlphaChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eColorTransformChanged) {
+        if (mDrawingState.colorTransform != s.colorTransform) {
+            ALOGV("%s: false [eColorTransformChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eBackgroundColorChanged) {
+        if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) {
+            ALOGV("%s: false [eBackgroundColorChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eMatrixChanged) {
+        if (mRequestedTransform.dsdx() != s.matrix.dsdx ||
+            mRequestedTransform.dtdy() != s.matrix.dtdy ||
+            mRequestedTransform.dtdx() != s.matrix.dtdx ||
+            mRequestedTransform.dsdy() != s.matrix.dsdy) {
+            ALOGV("%s: false [eMatrixChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eCornerRadiusChanged) {
+        if (mDrawingState.cornerRadius != s.cornerRadius) {
+            ALOGV("%s: false [eCornerRadiusChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+        if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) {
+            ALOGV("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eBufferTransformChanged) {
+        if (mDrawingState.bufferTransform != s.bufferTransform) {
+            ALOGV("%s: false [eBufferTransformChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eTransformToDisplayInverseChanged) {
+        if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) {
+            ALOGV("%s: false [eTransformToDisplayInverseChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eCropChanged) {
+        if (mDrawingState.crop != s.crop) {
+            ALOGV("%s: false [eCropChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eDataspaceChanged) {
+        if (mDrawingState.dataspace != s.dataspace) {
+            ALOGV("%s: false [eDataspaceChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eHdrMetadataChanged) {
+        if (mDrawingState.hdrMetadata != s.hdrMetadata) {
+            ALOGV("%s: false [eHdrMetadataChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eSidebandStreamChanged) {
+        if (mDrawingState.sidebandStream != s.sidebandStream) {
+            ALOGV("%s: false [eSidebandStreamChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eColorSpaceAgnosticChanged) {
+        if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) {
+            ALOGV("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eShadowRadiusChanged) {
+        if (mDrawingState.shadowRadius != s.shadowRadius) {
+            ALOGV("%s: false [eShadowRadiusChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eFixedTransformHintChanged) {
+        if (mDrawingState.fixedTransformHint != s.fixedTransformHint) {
+            ALOGV("%s: false [eFixedTransformHintChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eTrustedOverlayChanged) {
+        if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) {
+            ALOGV("%s: false [eTrustedOverlayChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eStretchChanged) {
+        StretchEffect temp = s.stretchEffect;
+        temp.sanitize();
+        if (mDrawingState.stretchEffect != temp) {
+            ALOGV("%s: false [eStretchChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eBufferCropChanged) {
+        if (mDrawingState.bufferCrop != s.bufferCrop) {
+            ALOGV("%s: false [eBufferCropChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eDestinationFrameChanged) {
+        if (mDrawingState.destinationFrame != s.destinationFrame) {
+            ALOGV("%s: false [eDestinationFrameChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eDimmingEnabledChanged) {
+        if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
+            ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    ALOGV("%s: true", __func__);
+    return true;
+}
+
+bool Layer::isHdrY410() const {
+    // pixel format is HDR Y410 masquerading as RGBA_1010102
+    return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
+            mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
+            mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
+}
+
+sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
+    // There's no need to get a CE Layer if the layer isn't going to draw anything.
+    if (hasSomethingToDraw()) {
+        return asLayerFE();
+    } else {
+        return nullptr;
+    }
+}
+
+const Layer::LayerSnapshot* Layer::getLayerSnapshot() const {
+    return mSnapshot.get();
+}
+
+Layer::LayerSnapshot* Layer::editLayerSnapshot() {
+    return mSnapshot.get();
+}
+const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
+    return mSnapshot.get();
+}
+
+void Layer::useSurfaceDamage() {
+    if (mFlinger->mForceFullDamage) {
+        surfaceDamageRegion = Region::INVALID_REGION;
+    } else {
+        surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
+    }
+}
+
+void Layer::useEmptyDamage() {
+    surfaceDamageRegion.clear();
+}
+
+bool Layer::isOpaque(const Layer::State& s) const {
+    // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
+    // layer's opaque flag.
+    if (!hasSomethingToDraw()) {
+        return false;
+    }
+
+    // if the layer has the opaque flag, then we're always opaque
+    if ((s.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque) {
+        return true;
+    }
+
+    // If the buffer has no alpha channel, then we are opaque
+    if (hasBufferOrSidebandStream() && isOpaqueFormat(getPixelFormat())) {
+        return true;
+    }
+
+    // Lastly consider the layer opaque if drawing a color with alpha == 1.0
+    return fillsColor() && getAlpha() == 1.0_hf;
+}
+
+bool Layer::canReceiveInput() const {
+    return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
+}
+
+bool Layer::isVisible() const {
+    if (!hasSomethingToDraw()) {
+        return false;
+    }
+
+    if (isHiddenByPolicy()) {
+        return false;
+    }
+
+    return getAlpha() > 0.0f || hasBlur();
+}
+
+void Layer::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;
+
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->gpuCompositionDoneFence = glDoneFence;
+        handle->compositorTiming = compositorTiming;
+    }
+
+    // Update mFrameTracker.
+    nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
+    mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
+
+    const auto outputLayer = findOutputLayerForDisplay(display);
+    if (outputLayer && outputLayer->requiresClientComposition()) {
+        nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
+        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               clientCompositionTimestamp,
+                                               FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+        // Update the SurfaceFrames in the drawing state
+        if (mDrawingState.bufferSurfaceFrameTX) {
+            mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
+        }
+        for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
+            surfaceFrame->setGpuComposition();
+        }
+    }
+
+    std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
+    if (frameReadyFence->isValid()) {
+        mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
+    } else {
+        // There was no fence for this frame, so assume that it was ready
+        // to be presented at the desired present time.
+        mFrameTracker.setFrameReadyTime(desiredPresentTime);
+    }
+
+    if (display) {
+        const Fps refreshRate = display->refreshRateConfigs().getActiveModePtr()->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, vote, gameMode);
+            mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               presentFence,
+                                               FrameTracer::FrameEvent::PRESENT_FENCE);
+            mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
+        } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+                   displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+            // The HWC doesn't support present fences, so use the present timestamp instead.
+            const nsecs_t presentTimestamp =
+                    mFlinger->getHwComposer().getPresentTimestamp(*displayId);
+
+            const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+            const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC();
+            const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod);
+
+            mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
+                                                 refreshRate, renderRate, vote, gameMode);
+            mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
+                                                   mCurrentFrameNumber, actualPresentTime,
+                                                   FrameTracer::FrameEvent::PRESENT_FENCE);
+            mFrameTracker.setActualPresentTime(actualPresentTime);
+        }
+    }
+
+    mFrameTracker.advanceFrame();
+    mBufferInfo.mFrameLatencyNeeded = false;
+}
+
+bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+    ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
+                          getDrawingState().frameNumber);
+
+    bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
+
+    if (refreshRequired) {
+        return refreshRequired;
+    }
+
+    // If the head buffer's acquire fence hasn't signaled yet, return and
+    // try again later
+    if (!fenceHasSignaled()) {
+        ATRACE_NAME("!fenceHasSignaled()");
+        mFlinger->onLayerUpdate();
+        return false;
+    }
+
+    updateTexImage(latchTime);
+    if (mDrawingState.buffer == nullptr) {
+        return false;
+    }
+
+    // Capture the old state of the layer for comparisons later
+    BufferInfo oldBufferInfo = mBufferInfo;
+    const bool oldOpacity = isOpaque(mDrawingState);
+    mPreviousFrameNumber = mCurrentFrameNumber;
+    mCurrentFrameNumber = mDrawingState.frameNumber;
+    gatherBufferInfo();
+
+    if (oldBufferInfo.mBuffer == nullptr) {
+        // the first time we receive a buffer, we need to trigger a
+        // geometry invalidation.
+        recomputeVisibleRegions = true;
+    }
+
+    if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
+        (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
+        (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
+        (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
+        recomputeVisibleRegions = true;
+    }
+
+    if (oldBufferInfo.mBuffer != nullptr) {
+        uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+        uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+        if (bufWidth != oldBufferInfo.mBuffer->getWidth() ||
+            bufHeight != oldBufferInfo.mBuffer->getHeight()) {
+            recomputeVisibleRegions = true;
+        }
+    }
+
+    if (oldOpacity != isOpaque(mDrawingState)) {
+        recomputeVisibleRegions = true;
     }
 
     return true;
 }
 
+bool Layer::hasReadyFrame() const {
+    return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
+}
+
+bool Layer::isProtected() const {
+    return (mBufferInfo.mBuffer != nullptr) &&
+            (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+}
+
+// As documented in libhardware header, formats in the range
+// 0x100 - 0x1FF are specific to the HAL implementation, and
+// are known to have no alpha channel
+// TODO: move definition for device-specific range into
+// hardware.h, instead of using hard-coded values here.
+#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
+
+bool Layer::isOpaqueFormat(PixelFormat format) {
+    if (HARDWARE_IS_DEVICE_FORMAT(format)) {
+        return true;
+    }
+    switch (format) {
+        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)
+    return true;
+}
+
+bool Layer::needsFiltering(const DisplayDevice* display) const {
+    if (!hasBufferOrSidebandStream()) {
+        return false;
+    }
+    const auto outputLayer = findOutputLayerForDisplay(display);
+    if (outputLayer == nullptr) {
+        return false;
+    }
+
+    // We need filtering if the sourceCrop rectangle size does not match the
+    // displayframe rectangle size (not a 1:1 render)
+    const auto& compositionState = outputLayer->getState();
+    const auto displayFrame = compositionState.displayFrame;
+    const auto sourceCrop = compositionState.sourceCrop;
+    return sourceCrop.getHeight() != displayFrame.getHeight() ||
+            sourceCrop.getWidth() != displayFrame.getWidth();
+}
+
+bool Layer::needsFilteringForScreenshots(const DisplayDevice* display,
+                                         const ui::Transform& inverseParentTransform) const {
+    if (!hasBufferOrSidebandStream()) {
+        return false;
+    }
+    const auto outputLayer = findOutputLayerForDisplay(display);
+    if (outputLayer == nullptr) {
+        return false;
+    }
+
+    // We need filtering if the sourceCrop rectangle size does not match the
+    // viewport rectangle size (not a 1:1 render)
+    const auto& compositionState = outputLayer->getState();
+    const ui::Transform& displayTransform = display->getTransform();
+    const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
+    // Undo the transformation of the displayFrame so that we're back into
+    // layer-stack space.
+    const Rect frame = inverseTransform.transform(compositionState.displayFrame);
+    const FloatRect sourceCrop = compositionState.sourceCrop;
+
+    int32_t frameHeight = frame.getHeight();
+    int32_t frameWidth = frame.getWidth();
+    // If the display transform had a rotational component then undo the
+    // rotation so that the orientation matches the source crop.
+    if (displayTransform.getOrientation() & ui::Transform::ROT_90) {
+        std::swap(frameHeight, frameWidth);
+    }
+    return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
+}
+
+void Layer::latchAndReleaseBuffer() {
+    if (hasReadyFrame()) {
+        bool ignored = false;
+        latchBuffer(ignored, systemTime());
+    }
+    releasePendingBuffer(systemTime());
+}
+
+PixelFormat Layer::getPixelFormat() const {
+    return mBufferInfo.mPixelFormat;
+}
+
+bool Layer::getTransformToDisplayInverse() const {
+    return mBufferInfo.mTransformToDisplayInverse;
+}
+
+Rect Layer::getBufferCrop() const {
+    // this is the crop rectangle that applies to the buffer
+    // itself (as opposed to the window)
+    if (!mBufferInfo.mCrop.isEmpty()) {
+        // if the buffer crop is defined, we use that
+        return mBufferInfo.mCrop;
+    } else if (mBufferInfo.mBuffer != nullptr) {
+        // otherwise we use the whole buffer
+        return mBufferInfo.mBuffer->getBounds();
+    } else {
+        // if we don't have a buffer yet, we use an empty/invalid crop
+        return Rect();
+    }
+}
+
+uint32_t Layer::getBufferTransform() const {
+    return mBufferInfo.mTransform;
+}
+
+ui::Dataspace Layer::getDataSpace() const {
+    return mDrawingState.dataspaceRequested ? getRequestedDataSpace() : ui::Dataspace::UNKNOWN;
+}
+
+ui::Dataspace Layer::getRequestedDataSpace() const {
+    return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace;
+}
+
+ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) {
+    ui::Dataspace updatedDataspace = dataspace;
+    // translate legacy dataspaces to modern dataspaces
+    switch (dataspace) {
+        case ui::Dataspace::SRGB:
+            updatedDataspace = ui::Dataspace::V0_SRGB;
+            break;
+        case ui::Dataspace::SRGB_LINEAR:
+            updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+            break;
+        case ui::Dataspace::JFIF:
+            updatedDataspace = ui::Dataspace::V0_JFIF;
+            break;
+        case ui::Dataspace::BT601_625:
+            updatedDataspace = ui::Dataspace::V0_BT601_625;
+            break;
+        case ui::Dataspace::BT601_525:
+            updatedDataspace = ui::Dataspace::V0_BT601_525;
+            break;
+        case ui::Dataspace::BT709:
+            updatedDataspace = ui::Dataspace::V0_BT709;
+            break;
+        default:
+            break;
+    }
+
+    return updatedDataspace;
+}
+
+sp<GraphicBuffer> Layer::getBuffer() const {
+    return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
+}
+
+void Layer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) const {
+    sp<GraphicBuffer> buffer = getBuffer();
+    if (!buffer) {
+        ALOGE("Buffer should not be null!");
+        return;
+    }
+    GLConsumer::computeTransformMatrix(outMatrix, buffer->getWidth(), buffer->getHeight(),
+                                       buffer->getPixelFormat(), mBufferInfo.mCrop,
+                                       mBufferInfo.mTransform, filteringEnabled);
+}
+
+void Layer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
+    mTransformHint = getFixedTransformHint();
+    if (mTransformHint == ui::Transform::ROT_INVALID) {
+        mTransformHint = displayTransformHint;
+    }
+}
+
+const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
+    return mBufferInfo.mBuffer;
+}
+
+bool Layer::setColor(const half3& color) {
+    if (mDrawingState.color.rgb == color) {
+        return false;
+    }
+
+    mDrawingState.sequence++;
+    mDrawingState.color.rgb = color;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::fillsColor() const {
+    return !hasBufferOrSidebandStream() && mDrawingState.color.r >= 0.0_hf &&
+            mDrawingState.color.g >= 0.0_hf && mDrawingState.color.b >= 0.0_hf;
+}
+
+bool Layer::hasBlur() const {
+    return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
+}
+
+void Layer::updateSnapshot(bool updateGeometry) {
+    if (!getCompositionEngineLayerFE()) {
+        return;
+    }
+
+    auto* snapshot = editLayerSnapshot();
+    if (updateGeometry) {
+        prepareBasicGeometryCompositionState();
+        prepareGeometryCompositionState();
+        snapshot->roundedCorner = getRoundedCornerState();
+        snapshot->stretchEffect = getStretchEffect();
+        snapshot->transformedBounds = mScreenBounds;
+        if (mEffectiveShadowRadius > 0.f) {
+            snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings;
+
+            // Note: this preserves existing behavior of shadowing the entire layer and not cropping
+            // it if transparent regions are present. This may not be necessary since shadows are
+            // typically cast by layers without transparent regions.
+            snapshot->shadowSettings.boundaries = mBounds;
+
+            const float casterAlpha = snapshot->alpha;
+            const bool casterIsOpaque =
+                    ((mBufferInfo.mBuffer != nullptr) && isOpaque(mDrawingState));
+
+            // If the casting layer is translucent, we need to fill in the shadow underneath the
+            // layer. Otherwise the generated shadow will only be shown around the casting layer.
+            snapshot->shadowSettings.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+            snapshot->shadowSettings.ambientColor *= casterAlpha;
+            snapshot->shadowSettings.spotColor *= casterAlpha;
+        }
+        snapshot->shadowSettings.length = mEffectiveShadowRadius;
+    }
+    snapshot->contentOpaque = isOpaque(mDrawingState);
+    snapshot->isHdrY410 = isHdrY410();
+    snapshot->bufferNeedsFiltering = bufferNeedsFiltering();
+    sp<Layer> p = mDrawingParent.promote();
+    if (p != nullptr) {
+        snapshot->transform = p->getTransform();
+    } else {
+        snapshot->transform.reset();
+    }
+    snapshot->bufferSize = getBufferSize(mDrawingState);
+    snapshot->externalTexture = mBufferInfo.mBuffer;
+    preparePerFrameCompositionState();
+}
+
+void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) {
+    mSnapshot->layerMetadata = parentMetadata;
+    mSnapshot->layerMetadata.merge(mDrawingState.metadata);
+    for (const sp<Layer>& child : mDrawingChildren) {
+        child->updateMetadataSnapshot(mSnapshot->layerMetadata);
+    }
+}
+
+void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
+                                           std::unordered_set<Layer*>& visited) {
+    if (visited.find(this) != visited.end()) {
+        ALOGW("Cycle containing layer %s detected in z-order relatives", getDebugName());
+        return;
+    }
+    visited.insert(this);
+
+    mSnapshot->relativeLayerMetadata = relativeLayerMetadata;
+
+    if (mDrawingState.zOrderRelatives.empty()) {
+        return;
+    }
+    LayerMetadata childRelativeLayerMetadata = mSnapshot->relativeLayerMetadata;
+    childRelativeLayerMetadata.merge(mSnapshot->layerMetadata);
+    for (wp<Layer> weakRelative : mDrawingState.zOrderRelatives) {
+        sp<Layer> relative = weakRelative.promote();
+        if (!relative) {
+            continue;
+        }
+        relative->updateRelativeMetadataSnapshot(childRelativeLayerMetadata, visited);
+    }
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 22bb866..8ace812 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -38,6 +38,7 @@
 #include <utils/Timers.h>
 
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <scheduler/Fps.h>
 #include <scheduler/Seamlessness.h>
 
@@ -48,12 +49,10 @@
 #include <vector>
 
 #include "Client.h"
-#include "ClientCache.h"
-#include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/HWComposer.h"
 #include "FrameTracker.h"
+#include "HwcSlotGenerator.h"
 #include "LayerVector.h"
-#include "RenderArea.h"
 #include "Scheduler/LayerInfo.h"
 #include "SurfaceFlinger.h"
 #include "Tracing/LayerTracing.h"
@@ -78,10 +77,6 @@
 class LayerDebugInfo;
 }
 
-namespace impl {
-class SurfaceInterceptor;
-}
-
 namespace frametimeline {
 class SurfaceFrame;
 } // namespace frametimeline
@@ -146,61 +141,63 @@
         bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
     };
 
+    // LayerSnapshot stores Layer state used by Composition Engine and Render Engine. Composition
+    // Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
+    // passed to Render Engine are created using properties stored on this struct.
+    //
+    // TODO(b/238781169) Implement LayerFE as a separate subclass. Migrate LayerSnapshot to that
+    // LayerFE subclass.
+    struct LayerSnapshot : public compositionengine::LayerFECompositionState {
+        int32_t sequence;
+        std::string name;
+        uint32_t textureName;
+        bool contentOpaque;
+        RoundedCornerState roundedCorner;
+        StretchEffect stretchEffect;
+        FloatRect transformedBounds;
+        renderengine::ShadowSettings shadowSettings;
+        bool premultipliedAlpha;
+        bool isHdrY410;
+        bool bufferNeedsFiltering;
+        ui::Transform transform;
+        Rect bufferSize;
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+        LayerMetadata layerMetadata;
+        LayerMetadata relativeLayerMetadata;
+    };
+
     using FrameRate = scheduler::LayerInfo::FrameRate;
     using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
 
     struct State {
-        Geometry active_legacy;
-        Geometry requested_legacy;
         int32_t z;
-
         ui::LayerStack layerStack;
-
         uint32_t flags;
-        uint8_t reserved[2];
         int32_t sequence; // changes when visible regions can change
         bool modified;
-
         // Crop is expressed in layer space coordinate.
         Rect crop;
-        Rect requestedCrop;
-
-        // the transparentRegion hint is a bit special, it's latched only
-        // when we receive a buffer -- this is because it's "content"
-        // dependent.
-        Region activeTransparentRegion_legacy;
-        Region requestedTransparentRegion_legacy;
-
         LayerMetadata metadata;
-
         // If non-null, a Surface this Surface's Z-order is interpreted relative to.
         wp<Layer> zOrderRelativeOf;
         bool isRelativeOf{false};
 
         // A list of surfaces whose Z-order is interpreted relative to ours.
         SortedVector<wp<Layer>> zOrderRelatives;
-
         half4 color;
         float cornerRadius;
         int backgroundBlurRadius;
-
         gui::WindowInfo inputInfo;
         wp<Layer> touchableRegionCrop;
 
-        // dataspace is only used by BufferStateLayer and EffectLayer
         ui::Dataspace dataspace;
+        bool dataspaceRequested;
 
-        // The fields below this point are only used by BufferStateLayer
         uint64_t frameNumber;
-        uint32_t width;
-        uint32_t height;
         ui::Transform transform;
-
         uint32_t bufferTransform;
         bool transformToDisplayInverse;
-
         Region transparentRegionHint;
-
         std::shared_ptr<renderengine::ExternalTexture> buffer;
         client_cache_t clientCacheId;
         sp<Fence> acquireFence;
@@ -208,11 +205,9 @@
         HdrMetadata hdrMetadata;
         Region surfaceDamageRegion;
         int32_t api;
-
         sp<NativeHandle> sidebandStream;
         mat4 colorTransform;
         bool hasColorTransform;
-
         // pointer to background color layer that, if set, appears below the buffer state layer
         // and the buffer state layer's children.  Z order will be set to
         // INT_MIN
@@ -237,7 +232,6 @@
 
         // Default frame rate compatibility used to set the layer refresh rate votetype.
         FrameRateCompatibility defaultFrameRateCompatibility;
-
         FrameRate frameRate;
 
         // The combined frame rate of parents / children of this layer
@@ -257,7 +251,6 @@
 
         // When the transaction was posted
         nsecs_t postTime;
-
         sp<ITransactionCompletedListener> releaseBufferListener;
         // SurfaceFrame that tracks the timeline of Transactions that contain a Buffer. Only one
         // such SurfaceFrame exists because only one buffer can be presented on the layer per vsync.
@@ -278,16 +271,11 @@
 
         // Whether or not this layer is a trusted overlay for input
         bool isTrustedOverlay;
-
         Rect bufferCrop;
         Rect destinationFrame;
-
         sp<IBinder> releaseBufferEndpoint;
-
         gui::DropInputMode dropInputMode;
-
         bool autoRefresh = false;
-
         bool dimmingEnabled = true;
     };
 
@@ -338,43 +326,17 @@
     static void miniDumpHeader(std::string& result);
 
     // Provide unique string for each class type in the Layer hierarchy
-    virtual const char* getType() const = 0;
+    virtual const char* getType() const { return "Layer"; }
 
     // true if this layer is visible, false otherwise
-    virtual bool isVisible() const = 0;
+    virtual bool isVisible() const;
 
-    virtual sp<Layer> createClone() = 0;
+    virtual sp<Layer> createClone();
 
-    // Geometry setting functions.
-    //
-    // The following group of functions are used to specify the layers
-    // bounds, and the mapping of the texture on to those bounds. According
-    // to various settings changes to them may apply immediately, or be delayed until
-    // a pending resize is completed by the producer submitting a buffer. For example
-    // if we were to change the buffer size, and update the matrix ahead of the
-    // new buffer arriving, then we would be stretching the buffer to a different
-    // aspect before and after the buffer arriving, which probably isn't what we wanted.
-    //
-    // The first set of geometry functions are controlled by the scaling mode, described
-    // in window.h. The scaling mode may be set by the client, as it submits buffers.
-    //
-    // Put simply, if our scaling mode is SCALING_MODE_FREEZE, then
-    // matrix updates will not be applied while a resize is pending
-    // and the size and transform will remain in their previous state
-    // until a new buffer is submitted. If the scaling mode is another value
-    // then the old-buffer will immediately be scaled to the pending size
-    // and the new matrix will be immediately applied following this scaling
-    // transformation.
-
-    // Set the default buffer size for the assosciated Producer, in pixels. This is
-    // also the rendered size of the layer prior to any transformations. Parent
-    // or local matrix transformations will not affect the size of the buffer,
-    // but may affect it's on-screen size or clipping.
-    virtual bool setSize(uint32_t w, uint32_t h);
     // Set a 2x2 transformation matrix on the layer. This transform
     // will be applied after parent transforms, but before any final
     // producer specified transform.
-    virtual bool setMatrix(const layer_state_t::matrix22_t& matrix);
+    bool setMatrix(const layer_state_t::matrix22_t& matrix);
 
     // This second set of geometry attributes are controlled by
     // setGeometryAppliesWithResize, and their default mode is to be
@@ -384,9 +346,9 @@
 
     // setPosition operates in parent buffer space (pre parent-transform) or display
     // space for top-level layers.
-    virtual bool setPosition(float x, float y);
+    bool setPosition(float x, float y);
     // Buffer space
-    virtual bool setCrop(const Rect& crop);
+    bool setCrop(const Rect& crop);
 
     // TODO(b/38182121): Could we eliminate the various latching modes by
     // using the layer hierarchy?
@@ -395,7 +357,7 @@
     virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
 
     virtual bool setAlpha(float alpha);
-    virtual bool setColor(const half3& /*color*/) { return false; };
+    bool setColor(const half3& /*color*/);
 
     // Set rounded corner radius for this layer and its children.
     //
@@ -407,7 +369,7 @@
     // is specified in pixels.
     virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
     virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
-    virtual bool setTransparentRegionHint(const Region& transparent);
+    bool setTransparentRegionHint(const Region& transparent);
     virtual bool setTrustedOverlay(bool);
     virtual bool setFlags(uint32_t flags, uint32_t mask);
     virtual bool setLayerStack(ui::LayerStack);
@@ -421,30 +383,25 @@
     virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
     virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; };
 
-    // Used only to set BufferStateLayer state
-    virtual bool setTransform(uint32_t /*transform*/) { return false; };
-    virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
-    virtual bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
-                           const BufferData& /* bufferData */, nsecs_t /* postTime */,
-                           nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
-                           std::optional<nsecs_t> /* dequeueTime */,
-                           const FrameTimelineInfo& /*info*/) {
-        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; };
-    virtual bool setApi(int32_t /*api*/) { return false; };
-    virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
-    virtual bool setTransactionCompletedListeners(
-            const std::vector<sp<CallbackHandle>>& /*handles*/);
+    bool setTransform(uint32_t /*transform*/);
+    bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
+    bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
+                   const BufferData& /* bufferData */, nsecs_t /* postTime */,
+                   nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+                   std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
+    bool setDataspace(ui::Dataspace /*dataspace*/);
+    bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
+    bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
+    bool setApi(int32_t /*api*/);
+    bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/);
+    bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/);
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
     virtual bool setColorSpaceAgnostic(const bool agnostic);
     virtual bool setDimmingEnabled(const bool dimmingEnabled);
     virtual bool setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility);
     virtual bool setFrameRateSelectionPriority(int32_t priority);
     virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
-    virtual void setAutoRefresh(bool /* autoRefresh */) {}
+    void setAutoRefresh(bool /* autoRefresh */);
     bool setDropInputMode(gui::DropInputMode);
 
     //  If the variable is not set on the layer, it traverses up the tree to inherit the frame
@@ -453,16 +410,19 @@
     //
     virtual FrameRateCompatibility getDefaultFrameRateCompatibility() const;
     //
-    virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
+    ui::Dataspace getDataSpace() const;
+    ui::Dataspace getRequestedDataSpace() const;
 
     virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
-    virtual compositionengine::LayerFECompositionState* editCompositionState();
+
+    const LayerSnapshot* getLayerSnapshot() const;
+    LayerSnapshot* editLayerSnapshot();
 
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
     // one empty rect.
-    virtual void useSurfaceDamage() {}
-    virtual void useEmptyDamage() {}
+    void useSurfaceDamage();
+    void useEmptyDamage();
     Region getVisibleRegion(const DisplayDevice*) const;
 
     /*
@@ -472,18 +432,18 @@
      * pixel format includes an alpha channel) and the "opaque" flag set
      * on the layer.  It does not examine the current plane alpha value.
      */
-    virtual bool isOpaque(const Layer::State&) const { return false; }
+    bool isOpaque(const Layer::State&) const;
 
     /*
      * Returns whether this layer can receive input.
      */
-    virtual bool canReceiveInput() const;
+    bool canReceiveInput() const;
 
     /*
      * isProtected - true if the layer may contain protected contents in the
      * GRALLOC_USAGE_PROTECTED sense.
      */
-    virtual bool isProtected() const { return false; }
+    bool isProtected() const;
 
     /*
      * isFixedSize - true if content has a fixed size
@@ -493,21 +453,19 @@
     /*
      * usesSourceCrop - true if content should use a source crop
      */
-    virtual bool usesSourceCrop() const { return false; }
+    bool usesSourceCrop() const { return hasBufferOrSidebandStream(); }
 
     // Most layers aren't created from the main thread, and therefore need to
     // grab the SF state lock to access HWC, but ContainerLayer does, so we need
     // to avoid grabbing the lock again to avoid deadlock
     virtual bool isCreatedFromMainThread() const { return false; }
 
-    uint32_t getActiveWidth(const Layer::State& s) const { return s.width; }
-    uint32_t getActiveHeight(const Layer::State& s) const { return s.height; }
     ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; }
-    virtual Region getActiveTransparentRegion(const Layer::State& s) const {
-        return s.activeTransparentRegion_legacy;
+    Region getActiveTransparentRegion(const Layer::State& s) const {
+        return s.transparentRegionHint;
     }
-    virtual Rect getCrop(const Layer::State& s) const { return s.crop; }
-    virtual bool needsFiltering(const DisplayDevice*) const { return false; }
+    Rect getCrop(const Layer::State& s) const { return s.crop; }
+    bool needsFiltering(const DisplayDevice*) const;
 
     // True if this layer requires filtering
     // This method is distinct from needsFiltering() in how the filter
@@ -518,27 +476,25 @@
     // different.
     // If the parent transform needs to be undone when capturing the layer, then
     // the inverse parent transform is also required.
-    virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
-        return false;
-    }
+    bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const;
 
-    virtual void updateCloneBufferInfo(){};
+    // from graphics API
+    ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+    void updateCloneBufferInfo();
+    uint64_t mPreviousFrameNumber = 0;
 
-    virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
-
-    virtual bool isHdrY410() const { return false; }
+    bool isHdrY410() const;
 
     /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual void onPostComposition(const DisplayDevice*,
-                                   const std::shared_ptr<FenceTime>& /*glDoneFence*/,
-                                   const std::shared_ptr<FenceTime>& /*presentFence*/,
-                                   const CompositorTiming&) {}
+    void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+                           const std::shared_ptr<FenceTime>& /*presentFence*/,
+                           const CompositorTiming&);
 
     // If a buffer was replaced this frame, release the former buffer
-    virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
+    void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/);
 
     /*
      * latchBuffer - called each time the screen is redrawn and returns whether
@@ -546,54 +502,55 @@
      * operation, so this should be set only if needed). Typically this is used
      * to figure out if the content or size of a surface has changed.
      */
-    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) {
-        return false;
-    }
+    bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/);
 
-    virtual void latchAndReleaseBuffer() {}
+    /*
+     * Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
+     * This is used if the buffer is just latched and releases to free up the buffer
+     * and will not be shown on screen.
+     * Should only be called on the main thread.
+     */
+    void latchAndReleaseBuffer();
 
     /*
      * returns the rectangle that crops the content of the layer and scales it
      * to the layer's size.
      */
-    virtual Rect getBufferCrop() const { return Rect(); }
+    Rect getBufferCrop() const;
 
     /*
      * Returns the transform applied to the buffer.
      */
-    virtual uint32_t getBufferTransform() const { return 0; }
+    uint32_t getBufferTransform() const;
 
-    virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
-    virtual const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const {
-        return mDrawingState.buffer;
-    };
+    sp<GraphicBuffer> getBuffer() const;
+    const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const;
 
-    virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
+    ui::Transform::RotationFlags getTransformHint() const { return mTransformHint; }
 
     /*
      * Returns if a frame is ready
      */
-    virtual bool hasReadyFrame() const { return false; }
+    bool hasReadyFrame() const;
 
     virtual int32_t getQueuedFrameCount() const { return 0; }
 
     /**
      * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
-     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+     * any buffer transformations. Returns Rect::INVALID_RECT if the layer has no buffer or the
+     * layer does not have a display frame and its parent is not bounded.
      */
-    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+    Rect getBufferSize(const Layer::State&) const;
 
     /**
      * Returns the source bounds. If the bounds are not defined, it is inferred from the
      * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
      * For the root layer, this is the display viewport size.
      */
-    virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
-        return parentBounds;
-    }
+    FloatRect computeSourceBounds(const FloatRect& parentBounds) const;
     virtual FrameRate getFrameRateForLayerTree() const;
 
-    virtual bool getTransformToDisplayInverse() const { return false; }
+    bool getTransformToDisplayInverse() const;
 
     // Returns how rounded corners should be drawn for this layer.
     // A layer can override its parent's rounded corner settings if the parent's rounded
@@ -602,31 +559,62 @@
 
     bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
 
-    virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
+    PixelFormat getPixelFormat() const;
     /**
-     * Return whether this layer needs an input info. For most layer types
-     * this is only true if they explicitly set an input-info but BufferLayer
-     * overrides this so we can generate input-info for Buffered layers that don't
-     * have them (for input occlusion detection checks).
+     * Return whether this layer needs an input info. We generate InputWindowHandles for all
+     * non-cursor buffered layers regardless of whether they have an InputChannel. This is to enable
+     * the InputDispatcher to do PID based occlusion detection.
      */
-    virtual bool needsInputInfo() const { return hasInputInfo(); }
+    bool needsInputInfo() const {
+        return (hasInputInfo() || hasBufferOrSidebandStream()) && !mPotentialCursor;
+    }
 
     // Implements RefBase.
     void onFirstRef() override;
 
+    struct BufferInfo {
+        nsecs_t mDesiredPresentTime;
+        std::shared_ptr<FenceTime> mFenceTime;
+        sp<Fence> mFence;
+        uint32_t mTransform{0};
+        ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
+        Rect mCrop;
+        uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
+        Region mSurfaceDamage;
+        HdrMetadata mHdrMetadata;
+        int mApi;
+        PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
+        bool mTransformToDisplayInverse{false};
+
+        std::shared_ptr<renderengine::ExternalTexture> mBuffer;
+        uint64_t mFrameNumber;
+        int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
+
+        bool mFrameLatencyNeeded{false};
+    };
+
+    BufferInfo mBufferInfo;
+
     // implements compositionengine::LayerFE
-    const compositionengine::LayerFECompositionState* getCompositionState() const override;
-    bool onPreComposition(nsecs_t) override;
-    void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
-    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
-            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
+    const compositionengine::LayerFECompositionState* getCompositionState() const;
+    bool fenceHasSignaled() const;
+    // Called before composition. updatingOutputGeometryThisFrame is used by ARC++'s Layer subclass.
+    bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame);
+    std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) const override;
+    void onLayerDisplayed(ftl::SharedFuture<FenceResult>);
 
     void setWasClientComposed(const sp<Fence>& fence) override {
         mLastClientCompositionFence = fence;
         mClearClientCompositionFenceOnLayerDisplayed = false;
     }
 
+    const LayerMetadata* getMetadata() const override { return &mSnapshot->layerMetadata; }
+
+    const LayerMetadata* getRelativeMetadata() const override {
+        return &mSnapshot->relativeLayerMetadata;
+    }
+
     const char* getDebugName() const override;
 
     bool setShadowRadius(float shadowRadius);
@@ -889,7 +877,9 @@
 
     bool mPendingHWCDestroy{false};
 
-    bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
+    bool backpressureEnabled() const {
+        return mDrawingState.flags & layer_state_t::eEnableBackpressure;
+    }
 
     bool setStretchEffect(const StretchEffect& effect);
     StretchEffect getStretchEffect() const;
@@ -898,17 +888,30 @@
     float getBorderWidth();
     const half4& getBorderColor();
 
-    virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
-    virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; }
-    virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
-    virtual std::string getPendingBufferCounterName() { return ""; }
-    virtual bool updateGeometry() { return false; }
+    bool setBufferCrop(const Rect& /* bufferCrop */);
+    bool setDestinationFrame(const Rect& /* destinationFrame */);
+    // See mPendingBufferTransactions
+    void decrementPendingBufferCount();
+    std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; }
+    std::string getPendingBufferCounterName() { return mBlastTransactionName; }
+    bool updateGeometry();
 
-    virtual bool simpleBufferUpdate(const layer_state_t&) const { return false; }
+    bool simpleBufferUpdate(const layer_state_t&) const;
+
+    static bool isOpaqueFormat(PixelFormat format);
+
+    // Updates the LayerSnapshot. This must be called prior to sending layer data to
+    // CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or
+    // Layer::prepareClientComposition).
+    //
+    // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
+    // CompositionEngine to create a single path for composing layers.
+    void updateSnapshot(bool updateGeometry);
+    void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
+    void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
+                                        std::unordered_set<Layer*>& visited);
 
 protected:
-    friend class impl::SurfaceInterceptor;
-
     // For unit tests
     friend class TestableSurfaceFlinger;
     friend class FpsReporterTest;
@@ -918,11 +921,12 @@
     friend class TransactionSurfaceFrameTest;
 
     virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
-    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
-            compositionengine::LayerFE::ClientCompositionTargetSettings&);
-    virtual void preparePerFrameCompositionState();
+    void preparePerFrameCompositionState();
+    void preparePerFrameBufferCompositionState();
+    void preparePerFrameEffectsCompositionState();
     virtual void commitTransaction(State& stateToCommit);
-    virtual void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&) {}
+    void gatherBufferInfo();
+    void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
 
     sp<compositionengine::LayerFE> asLayerFE() const;
     sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
@@ -940,7 +944,8 @@
     // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
     // the settings clears the content with a solid black fill.
     void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
-    void prepareShadowClientComposition(LayerFE::LayerSettings& caster, const Rect& layerStackRect);
+    void prepareShadowClientComposition(LayerFE::LayerSettings& caster,
+                                        const Rect& layerStackRect) const;
 
     void prepareBasicGeometryCompositionState();
     void prepareGeometryCompositionState();
@@ -974,7 +979,7 @@
      * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input
      * in this layer's space, regardless of the specified crop layer.
      */
-    virtual Rect getInputBounds() const;
+    Rect getInputBounds() const;
 
     // constant
     sp<SurfaceFlinger> mFlinger;
@@ -1045,7 +1050,14 @@
     sp<Fence> mLastClientCompositionFence;
     bool mClearClientCompositionFenceOnLayerDisplayed = false;
 private:
-    virtual void setTransformHint(ui::Transform::RotationFlags) {}
+    friend class SlotGenerationTest;
+    friend class TransactionFrameTracerTest;
+    friend class TransactionSurfaceFrameTest;
+
+    bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
+    bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
+
+    std::atomic<bool> mSidebandStreamChanged{false};
 
     // Returns true if the layer can draw shadows on its border.
     virtual bool canDrawShadows() const { return true; }
@@ -1090,6 +1102,52 @@
     // Fills in the frame and transform info for the gui::WindowInfo.
     void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay);
 
+    // Computes the transform matrix using the setFilteringEnabled to determine whether the
+    // transform matrix should be computed for use with bilinear filtering.
+    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) const;
+
+    inline void tracePendingBufferCount(int32_t pendingBuffers);
+
+    // Latch sideband stream and returns true if the dirty region should be updated.
+    bool latchSidebandStream(bool& recomputeVisibleRegions);
+
+    bool hasFrameUpdate() const;
+
+    void updateTexImage(nsecs_t latchTime);
+
+    // Crop that applies to the buffer
+    Rect computeBufferCrop(const State& s);
+
+    bool willPresentCurrentTransaction() const;
+
+    // Returns true if the transformed buffer size does not match the layer size and we need
+    // to apply filtering.
+    bool bufferNeedsFiltering() const;
+
+    void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+                                   const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+                                   const sp<Fence>& releaseFence,
+                                   uint32_t currentMaxAcquiredBufferCount);
+
+    std::optional<compositionengine::LayerFE::LayerSettings> prepareClientCompositionInternal(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+    // Returns true if there is a valid color to fill.
+    bool fillsColor() const;
+    // Returns true if this layer has a blur value.
+    bool hasBlur() const;
+    bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); }
+    bool hasBufferOrSidebandStream() const {
+        return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr));
+    }
+
+    bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); }
+    void prepareBufferStateClientComposition(
+            compositionengine::LayerFE::LayerSettings&,
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+    void prepareEffectsClientComposition(
+            compositionengine::LayerFE::LayerSettings&,
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
     // a transform from the current layer coordinate space to display(screen) coordinate space.
@@ -1139,6 +1197,49 @@
     bool mBorderEnabled = false;
     float mBorderWidth;
     half4 mBorderColor;
+
+    void setTransformHint(ui::Transform::RotationFlags);
+
+    const uint32_t mTextureName;
+
+    // Transform hint provided to the producer. This must be accessed holding
+    // the mStateLock.
+    ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+
+    ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
+    uint64_t mPreviousReleasedFrameNumber = 0;
+
+    uint64_t mPreviousBarrierFrameNumber = 0;
+
+    bool mReleasePreviousBuffer = false;
+
+    // Stores the last set acquire fence signal time used to populate the callback handle's acquire
+    // time.
+    std::variant<nsecs_t, sp<Fence>> mCallbackHandleAcquireTimeOrFence = -1;
+
+    std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
+    // An upper bound on the number of SurfaceFrames in the pending classifications deque.
+    static constexpr int kPendingClassificationMaxSurfaceFrames = 25;
+
+    const std::string mBlastTransactionName{"BufferTX - " + mName};
+    // This integer is incremented everytime a buffer arrives at the server for this layer,
+    // and decremented when a buffer is dropped or latched. When changed the integer is exported
+    // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is
+    // possible to see when a buffer arrived at the server, and in which frame it latched.
+    //
+    // You can understand the trace this way:
+    //     - If the integer increases, a buffer arrived at the server.
+    //     - If the integer decreases in latchBuffer, that buffer was latched
+    //     - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
+    std::atomic<int32_t> mPendingBufferTransactions{0};
+
+    // Contains requested position and matrix updates. This will be applied if the client does
+    // not specify a destination frame.
+    ui::Transform mRequestedTransform;
+
+    sp<HwcSlotGenerator> mHwcSlotGenerator;
+
+    std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>();
 };
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index e17b01f..6bc7dc1 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -18,7 +18,6 @@
 #include <ui/Transform.h>
 
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 #include "LayerRenderArea.h"
 #include "SurfaceFlinger.h"
@@ -110,7 +109,7 @@
         // layer which has no properties set and which does not draw.
         //  We hold the statelock as the reparent-for-drawing operation modifies the
         //  hierarchy and there could be readers on Binder threads, like dump.
-        sp<EffectLayer> screenshotParentLayer = mFlinger.getFactory().createEffectLayer(
+        auto screenshotParentLayer = mFlinger.getFactory().createEffectLayer(
                 {&mFlinger, nullptr, "Screenshot Parent"s, ISurfaceComposerClient::eNoColorFill,
                  LayerMetadata()});
         {
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 2ece51c..6011d0d 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -2,6 +2,7 @@
 alecmouri@google.com
 chaviw@google.com
 lpy@google.com
+pdwilliams@google.com
 racarr@google.com
 scroggo@google.com
-vishnun@google.com
\ No newline at end of file
+vishnun@google.com
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 5705255..8dd3b0f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -203,25 +203,14 @@
         return 0.0f;
     }
 
-    // (b/133849373) ROT_90 screencap images produced upside down
-    auto area = sample_area;
-    if (orientation & ui::Transform::ROT_90) {
-        area.top = height - area.top;
-        area.bottom = height - area.bottom;
-        std::swap(area.top, area.bottom);
-
-        area.left = width - area.left;
-        area.right = width - area.right;
-        std::swap(area.left, area.right);
-    }
-
-    const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
+    const uint32_t pixelCount =
+            (sample_area.bottom - sample_area.top) * (sample_area.right - sample_area.left);
     uint32_t accumulatedLuma = 0;
 
     // Calculates luma with approximation of Rec. 709 primaries
-    for (int32_t row = area.top; row < area.bottom; ++row) {
+    for (int32_t row = sample_area.top; row < sample_area.bottom; ++row) {
         const uint32_t* rowBase = data + row * stride;
-        for (int32_t column = area.left; column < area.right; ++column) {
+        for (int32_t column = sample_area.left; column < sample_area.right; ++column) {
             uint32_t pixel = rowBase[column];
             const uint32_t r = pixel & 0xFF;
             const uint32_t g = (pixel >> 8) & 0xFF;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 747032b..4af1f5c 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -188,10 +188,10 @@
 
 VSyncSource::VSyncData DispSyncSource::getLatestVSyncData() const {
     std::lock_guard lock(mVsyncMutex);
-    nsecs_t expectedPresentTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
+    nsecs_t expectedPresentationTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
             systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
-    nsecs_t deadline = expectedPresentTime - mWorkDuration.get().count() - mReadyDuration.count();
-    return {expectedPresentTime, deadline};
+    nsecs_t deadline = expectedPresentationTime - mReadyDuration.count();
+    return {expectedPresentationTime, deadline};
 }
 
 void DispSyncSource::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index d3f53c1..a6cd47b 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -237,16 +237,13 @@
 
 EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
                          android::frametimeline::TokenManager* tokenManager,
-                         InterceptVSyncsCallback interceptVSyncsCallback,
                          ThrottleVsyncCallback throttleVsyncCallback,
                          GetVsyncPeriodFunction getVsyncPeriodFunction)
       : mVSyncSource(std::move(vsyncSource)),
         mTokenManager(tokenManager),
-        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
         mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
         mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)),
         mThreadName(mVSyncSource->getName()) {
-
     LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
             "getVsyncPeriodFunction must not be null");
 
@@ -443,21 +440,13 @@
             event = mPendingEvents.front();
             mPendingEvents.pop_front();
 
-            switch (event->header.type) {
-                case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
-                    if (event->hotplug.connected && !mVSyncState) {
-                        mVSyncState.emplace(event->header.displayId);
-                    } else if (!event->hotplug.connected && mVSyncState &&
-                               mVSyncState->displayId == event->header.displayId) {
-                        mVSyncState.reset();
-                    }
-                    break;
-
-                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
-                    if (mInterceptVSyncsCallback) {
-                        mInterceptVSyncsCallback(event->header.timestamp);
-                    }
-                    break;
+            if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
+                if (event->hotplug.connected && !mVSyncState) {
+                    mVSyncState.emplace(event->header.displayId);
+                } else if (!event->hotplug.connected && mVSyncState &&
+                           mVSyncState->displayId == event->header.displayId) {
+                    mVSyncState.reset();
+                }
             }
         }
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index d85d140..7a5a348 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -161,12 +161,11 @@
 
 class EventThread : public android::EventThread, private VSyncSource::Callback {
 public:
-    using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
     using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
     using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
 
-    EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
-                ThrottleVsyncCallback, GetVsyncPeriodFunction);
+    EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, ThrottleVsyncCallback,
+                GetVsyncPeriodFunction);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
@@ -225,7 +224,6 @@
     const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
     frametimeline::TokenManager* const mTokenManager;
 
-    const InterceptVSyncsCallback mInterceptVSyncsCallback;
     const ThrottleVsyncCallback mThrottleVsyncCallback;
     const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
     const char* const mThreadName;
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
deleted file mode 100644
index 760a4ee..0000000
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 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 <mutex>
-
-#include "EventThread.h"
-
-namespace android {
-
-/**
- * VSync signals used during SurfaceFlinger trace playback (traces we captured
- * with SurfaceInterceptor).
- */
-class InjectVSyncSource final : public VSyncSource {
-public:
-    ~InjectVSyncSource() override = default;
-
-    void setCallback(VSyncSource::Callback* callback) override {
-        std::lock_guard<std::mutex> lock(mCallbackMutex);
-        mCallback = callback;
-    }
-
-    void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
-                           nsecs_t deadlineTimestamp) {
-        std::lock_guard<std::mutex> lock(mCallbackMutex);
-        if (mCallback) {
-            mCallback->onVSyncEvent(when, {expectedVSyncTimestamp, deadlineTimestamp});
-        }
-    }
-
-    const char* getName() const override { return "inject"; }
-    void setVSyncEnabled(bool) override {}
-    void setDuration(std::chrono::nanoseconds, std::chrono::nanoseconds) override {}
-    VSyncData getLatestVSyncData() const override { return {}; }
-    void dump(std::string&) const override {}
-
-private:
-    std::mutex mCallbackMutex;
-    VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index e1ac301..ae10ff4 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -57,37 +57,6 @@
         mLooper(sp<Looper>::make(kAllowNonCallbacks)),
         mHandler(std::move(handler)) {}
 
-// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
-// and remove the EventThread from MessageQueue
-void MessageQueue::setInjector(sp<EventThreadConnection> connection) {
-    auto& tube = mInjector.tube;
-
-    if (const int fd = tube.getFd(); fd >= 0) {
-        mLooper->removeFd(fd);
-    }
-
-    if (connection) {
-        // The EventThreadConnection is retained when disabling injection, so avoid subsequently
-        // stealing invalid FDs. Note that the stolen FDs are kept open.
-        if (tube.getFd() < 0) {
-            connection->stealReceiveChannel(&tube);
-        } else {
-            ALOGW("Recycling channel for VSYNC injection.");
-        }
-
-        mLooper->addFd(
-                tube.getFd(), 0, Looper::EVENT_INPUT,
-                [](int, int, void* data) {
-                    reinterpret_cast<MessageQueue*>(data)->injectorCallback();
-                    return 1; // Keep registration.
-                },
-                this);
-    }
-
-    std::lock_guard lock(mInjector.mutex);
-    mInjector.connection = std::move(connection);
-}
-
 void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
     ATRACE_CALL();
     // Trace VSYNC-sf
@@ -158,18 +127,22 @@
     mLooper->sendMessage(handler, Message());
 }
 
+void MessageQueue::scheduleConfigure() {
+    struct ConfigureHandler : MessageHandler {
+        explicit ConfigureHandler(ICompositor& compositor) : compositor(compositor) {}
+
+        void handleMessage(const Message&) override { compositor.configure(); }
+
+        ICompositor& compositor;
+    };
+
+    // TODO(b/241285876): Batch configure tasks that happen within some duration.
+    postMessage(sp<ConfigureHandler>::make(mCompositor));
+}
+
 void MessageQueue::scheduleFrame() {
     ATRACE_CALL();
 
-    {
-        std::lock_guard lock(mInjector.mutex);
-        if (CC_UNLIKELY(mInjector.connection)) {
-            ALOGD("%s while injecting VSYNC", __func__);
-            mInjector.connection->requestNextVsync();
-            return;
-        }
-    }
-
     std::lock_guard lock(mVsync.mutex);
     mVsync.scheduledFrameTime =
             mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
@@ -177,22 +150,6 @@
                                            .earliestVsync = mVsync.lastCallbackTime.ns()});
 }
 
-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) {
-                auto& vsync = buffer[i].vsync.vsyncData;
-                mHandler->dispatchFrame(VsyncId{vsync.preferredVsyncId()},
-                                        TimePoint::fromNs(
-                                                vsync.preferredExpectedPresentationTime()));
-                break;
-            }
-        }
-    }
-}
-
 auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
     if (mHandler->isFramePending()) {
         return Clock::now();
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 506f27b..04de492 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -38,6 +38,7 @@
 namespace android {
 
 struct ICompositor {
+    virtual void configure() = 0;
     virtual bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) = 0;
     virtual void composite(TimePoint frameTime, VsyncId) = 0;
     virtual void sample() = 0;
@@ -75,9 +76,9 @@
     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 scheduleConfigure() = 0;
     virtual void scheduleFrame() = 0;
 
     using Clock = std::chrono::steady_clock;
@@ -130,16 +131,7 @@
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
-    struct Injector {
-        gui::BitTube tube;
-        std::mutex mutex;
-        sp<EventThreadConnection> connection GUARDED_BY(mutex);
-    };
-
     Vsync mVsync;
-    Injector mInjector;
-
-    void injectorCallback();
 
 public:
     explicit MessageQueue(ICompositor&);
@@ -147,11 +139,11 @@
     void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                    std::chrono::nanoseconds workDuration) override;
     void setDuration(std::chrono::nanoseconds workDuration) override;
-    void setInjector(sp<EventThreadConnection>) override;
 
     void waitMessage() override;
     void postMessage(sp<MessageHandler>&&) override;
 
+    void scheduleConfigure() override;
     void scheduleFrame() override;
 
     std::optional<Clock::time_point> getScheduledFrameTime() const override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index ca83496..30483a2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -27,6 +27,8 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <ftl/enum.h>
+#include <ftl/fake_guard.h>
+#include <ftl/match.h>
 #include <utils/Trace.h>
 
 #include "../SurfaceFlingerProperties.h"
@@ -40,27 +42,13 @@
 
 struct RefreshRateScore {
     DisplayModeIterator modeIt;
-    float score;
+    float overallScore;
+    struct {
+        float modeBelowThreshold;
+        float modeAboveThreshold;
+    } fixedRateBelowThresholdLayersScore;
 };
 
-template <typename Iterator>
-const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) {
-    const auto it =
-            std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) {
-                const auto& [modeIt, score] = current;
-
-                std::string name = to_string(modeIt->second->getFps());
-                ALOGV("%s scores %.2f", name.c_str(), score);
-
-                ATRACE_INT(name.c_str(), static_cast<int>(std::round(score * 100)));
-
-                constexpr float kEpsilon = 0.0001f;
-                return score > max.score * (1 + kEpsilon);
-            });
-
-    return it->modeIt->second;
-}
-
 constexpr RefreshRateConfigs::GlobalSignals kNoSignals;
 
 std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
@@ -130,8 +118,50 @@
     return false;
 }
 
+std::string toString(const RefreshRateConfigs::PolicyVariant& policy) {
+    using namespace std::string_literals;
+
+    return ftl::match(
+            policy,
+            [](const RefreshRateConfigs::DisplayManagerPolicy& policy) {
+                return "DisplayManagerPolicy"s + policy.toString();
+            },
+            [](const RefreshRateConfigs::OverridePolicy& policy) {
+                return "OverridePolicy"s + policy.toString();
+            },
+            [](RefreshRateConfigs::NoOverridePolicy) { return "NoOverridePolicy"s; });
+}
+
 } // namespace
 
+struct RefreshRateConfigs::RefreshRateScoreComparator {
+    bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
+        const auto& [modeIt, overallScore, _] = lhs;
+
+        std::string name = to_string(modeIt->second->getFps());
+        ALOGV("%s sorting scores %.2f", name.c_str(), overallScore);
+
+        ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
+
+        constexpr float kEpsilon = 0.0001f;
+        if (std::abs(overallScore - rhs.overallScore) > kEpsilon) {
+            return overallScore > rhs.overallScore;
+        }
+
+        // If overallScore tie we will pick the higher refresh rate if
+        // high refresh rate is the priority else the lower refresh rate.
+        if (refreshRateOrder == RefreshRateOrder::Descending) {
+            using fps_approx_ops::operator>;
+            return modeIt->second->getFps() > rhs.modeIt->second->getFps();
+        } else {
+            using fps_approx_ops::operator<;
+            return modeIt->second->getFps() < rhs.modeIt->second->getFps();
+        }
+    }
+
+    const RefreshRateOrder refreshRateOrder;
+};
+
 std::string RefreshRateConfigs::Policy::toString() const {
     return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
                               ", primaryRange=%s, appRequestRange=%s}",
@@ -151,31 +181,6 @@
     return {quotient, remainder};
 }
 
-bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, Fps refreshRate) const {
-    using namespace fps_approx_ops;
-
-    switch (layer.vote) {
-        case LayerVoteType::ExplicitExactOrMultiple:
-        case LayerVoteType::Heuristic:
-            if (mConfig.frameRateMultipleThreshold != 0 &&
-                refreshRate >= 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.
-                return false;
-            }
-            break;
-        case LayerVoteType::ExplicitDefault:
-        case LayerVoteType::ExplicitExact:
-        case LayerVoteType::Max:
-        case LayerVoteType::Min:
-        case LayerVoteType::NoVote:
-            break;
-    }
-    return true;
-}
-
 float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
                                                                     Fps refreshRate) const {
     constexpr float kScoreForFractionalPairs = .8f;
@@ -238,22 +243,22 @@
     return 0;
 }
 
+float RefreshRateConfigs::calculateRefreshRateScoreForFps(Fps refreshRate) const {
+    const float ratio =
+            refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue();
+    // Use ratio^2 to get a lower score the more we get further from peak
+    return ratio * ratio;
+}
+
 float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
                                                     bool isSeamlessSwitch) const {
-    if (!isVoteAllowed(layer, refreshRate)) {
-        return 0;
-    }
-
     // Slightly prefer seamless switches.
     constexpr float kSeamedSwitchPenalty = 0.95f;
     const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
 
     // If the layer wants Max, give higher score to the higher refresh rate
     if (layer.vote == LayerVoteType::Max) {
-        const auto& maxRefreshRate = mAppRequestRefreshRates.back()->second;
-        const auto ratio = refreshRate.getValue() / maxRefreshRate->getFps().getValue();
-        // use ratio^2 to get a lower score the more we get further from peak
-        return ratio * ratio;
+        return calculateRefreshRateScoreForFps(refreshRate);
     }
 
     if (layer.vote == LayerVoteType::ExplicitExact) {
@@ -282,34 +287,44 @@
             kNonExactMatchingPenalty;
 }
 
-auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                            GlobalSignals signals) const
-        -> std::pair<DisplayModePtr, GlobalSignals> {
+auto RefreshRateConfigs::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
+                                               GlobalSignals signals) const
+        -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
     std::lock_guard lock(mLock);
 
-    if (mGetBestRefreshRateCache &&
-        mGetBestRefreshRateCache->arguments == std::make_pair(layers, signals)) {
-        return mGetBestRefreshRateCache->result;
+    if (mGetRankedRefreshRatesCache &&
+        mGetRankedRefreshRatesCache->arguments == std::make_pair(layers, signals)) {
+        return mGetRankedRefreshRatesCache->result;
     }
 
-    const auto result = getBestRefreshRateLocked(layers, signals);
-    mGetBestRefreshRateCache = GetBestRefreshRateCache{{layers, signals}, result};
+    const auto result = getRankedRefreshRatesLocked(layers, signals);
+    mGetRankedRefreshRatesCache = GetRankedRefreshRatesCache{{layers, signals}, result};
     return result;
 }
 
-auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
-                                                  GlobalSignals signals) const
-        -> std::pair<DisplayModePtr, GlobalSignals> {
+auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
+                                                     GlobalSignals signals) const
+        -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
+    using namespace fps_approx_ops;
     ATRACE_CALL();
     ALOGV("%s: %zu layers", __func__, layers.size());
 
+    const auto& activeMode = *getActiveModeItLocked()->second;
+
+    // Keep the display at max refresh rate for the duration of powering on the display.
+    if (signals.powerOnImminent) {
+        ALOGV("Power On Imminent");
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
+                GlobalSignals{.powerOnImminent = true}};
+    }
+
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
     int explicitDefaultVoteLayers = 0;
     int explicitExactOrMultipleVoteLayers = 0;
     int explicitExact = 0;
-    float maxExplicitWeight = 0;
     int seamedFocusedLayers = 0;
 
     for (const auto& layer : layers) {
@@ -325,15 +340,12 @@
                 break;
             case LayerVoteType::ExplicitDefault:
                 explicitDefaultVoteLayers++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
                 break;
             case LayerVoteType::ExplicitExactOrMultiple:
                 explicitExactOrMultipleVoteLayers++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
                 break;
             case LayerVoteType::ExplicitExact:
                 explicitExact++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
                 break;
             case LayerVoteType::Heuristic:
                 break;
@@ -349,6 +361,7 @@
 
     const Policy* policy = getCurrentPolicyLocked();
     const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
+
     // 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.
@@ -356,14 +369,15 @@
     // of the current mode, in order to prevent unnecessary seamed mode switches
     // (e.g. when pausing a video playback).
     const auto anchorGroup =
-            seamedFocusedLayers > 0 ? mActiveModeIt->second->getGroup() : defaultMode->getGroup();
+            seamedFocusedLayers > 0 ? activeMode.getGroup() : defaultMode->getGroup();
 
     // 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 (signals.touch && !hasExplicitVoteLayers) {
-        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-        ALOGV("TouchBoost - choose %s", to_string(max->getFps()).c_str());
-        return {max, GlobalSignals{.touch = true}};
+        ALOGV("Touch Boost");
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
+                GlobalSignals{.touch = true}};
     }
 
     // If the primary range consists of a single refresh rate then we can only
@@ -373,22 +387,25 @@
             isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
 
     if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
-        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
-        ALOGV("Idle - choose %s", to_string(min->getFps()).c_str());
-        return {min, GlobalSignals{.idle = true}};
+        ALOGV("Idle");
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
+                GlobalSignals{.idle = true}};
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
-        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-        ALOGV("no layers with votes - choose %s", to_string(max->getFps()).c_str());
-        return {max, kNoSignals};
+        ALOGV("No layers with votes");
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
+                kNoSignals};
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
-        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
-        ALOGV("all layers Min - choose %s", to_string(min->getFps()).c_str());
-        return {min, kNoSignals};
+        ALOGV("All layers Min");
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
+                kNoSignals};
     }
 
     // Find the best refresh rate based on score
@@ -409,14 +426,14 @@
 
         const auto weight = layer.weight;
 
-        for (auto& [modeIt, score] : scores) {
+        for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
             const auto& [id, mode] = *modeIt;
-            const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup();
+            const bool isSeamlessSwitch = mode->getGroup() == activeMode.getGroup();
 
             if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
                 ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
                       formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
-                      to_string(*mActiveModeIt->second).c_str());
+                      to_string(activeMode).c_str());
                 continue;
             }
 
@@ -425,7 +442,7 @@
                 ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
                       " Current mode = %s",
                       formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
-                      to_string(*mActiveModeIt->second).c_str());
+                      to_string(activeMode).c_str());
                 continue;
             }
 
@@ -437,7 +454,7 @@
             const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
                 ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
-                      to_string(*mode).c_str(), to_string(*mActiveModeIt->second).c_str());
+                      to_string(*mode).c_str(), to_string(activeMode).c_str());
                 continue;
             }
 
@@ -451,32 +468,117 @@
                 continue;
             }
 
-            const auto layerScore =
+            const float layerScore =
                     calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
-            ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
-                  to_string(mode->getFps()).c_str(), layerScore);
+            const float weightedLayerScore = weight * layerScore;
 
-            score += weight * layerScore;
+            // Layer with fixed source has a special consideration which depends on the
+            // mConfig.frameRateMultipleThreshold. We don't want these layers to score
+            // refresh rates above the threshold, but we also don't want to favor the lower
+            // ones by having a greater number of layers scoring them. Instead, we calculate
+            // the score independently for these layers and later decide which
+            // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not
+            // score 120 Hz, but desired 60 fps should contribute to the score.
+            const bool fixedSourceLayer = [](LayerVoteType vote) {
+                switch (vote) {
+                    case LayerVoteType::ExplicitExactOrMultiple:
+                    case LayerVoteType::Heuristic:
+                        return true;
+                    case LayerVoteType::NoVote:
+                    case LayerVoteType::Min:
+                    case LayerVoteType::Max:
+                    case LayerVoteType::ExplicitDefault:
+                    case LayerVoteType::ExplicitExact:
+                        return false;
+                }
+            }(layer.vote);
+            const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 &&
+                    layer.desiredRefreshRate <
+                            Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
+            if (fixedSourceLayer && layerBelowThreshold) {
+                const bool modeAboveThreshold =
+                        mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+                if (modeAboveThreshold) {
+                    ALOGV("%s gives %s fixed source (above threshold) score of %.4f",
+                          formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
+                          layerScore);
+                    fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
+                } else {
+                    ALOGV("%s gives %s fixed source (below threshold) score of %.4f",
+                          formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
+                          layerScore);
+                    fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
+                }
+            } else {
+                ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
+                      to_string(mode->getFps()).c_str(), layerScore);
+                overallScore += weightedLayerScore;
+            }
         }
     }
 
-    // Now that we scored all the refresh rates we need to pick the one that got the highest score.
-    // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
-    // or the lower otherwise.
-    const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0
-            ? getMaxScoreRefreshRate(scores.rbegin(), scores.rend())
-            : getMaxScoreRefreshRate(scores.begin(), scores.end());
+    // We want to find the best refresh rate without the fixed source layers,
+    // so we could know whether we should add the modeAboveThreshold scores or not.
+    // If the best refresh rate is already above the threshold, it means that
+    // some non-fixed source layers already scored it, so we can just add the score
+    // for all fixed source layers, even the ones that are above the threshold.
+    const bool maxScoreAboveThreshold = [&] {
+        if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) {
+            return false;
+        }
+
+        const auto maxScoreIt =
+                std::max_element(scores.begin(), scores.end(),
+                                 [](RefreshRateScore max, RefreshRateScore current) {
+                                     const auto& [modeIt, overallScore, _] = current;
+                                     return overallScore > max.overallScore;
+                                 });
+        ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for "
+              "refresh rate multiples",
+              to_string(maxScoreIt->modeIt->second->getFps()).c_str(),
+              maxScoreAboveThreshold ? "above" : "below");
+        return maxScoreIt->modeIt->second->getFps() >=
+                Fps::fromValue(mConfig.frameRateMultipleThreshold);
+    }();
+
+    // Now we can add the fixed rate layers score
+    for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+        overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
+        if (maxScoreAboveThreshold) {
+            overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
+        }
+        ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(),
+              overallScore);
+    }
+
+    // Now that we scored all the refresh rates we need to pick the one that got the highest
+    // overallScore. Sort the scores based on their overallScore in descending order of priority.
+    const RefreshRateOrder refreshRateOrder =
+            maxVoteLayers > 0 ? RefreshRateOrder::Descending : RefreshRateOrder::Ascending;
+    std::sort(scores.begin(), scores.end(),
+              RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
+    std::vector<RefreshRateRanking> rankedRefreshRates;
+    rankedRefreshRates.reserve(scores.size());
+
+    std::transform(scores.begin(), scores.end(), back_inserter(rankedRefreshRates),
+                   [](const RefreshRateScore& score) {
+                       return RefreshRateRanking{score.modeIt->second, score.overallScore};
+                   });
+
+    const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
+        return score.overallScore == 0;
+    });
 
     if (primaryRangeIsSingleRate) {
         // If we never scored any layers, then choose the rate from the primary
         // 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; })) {
-            const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-            ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str());
-            return {max, kNoSignals};
+        if (noLayerScore) {
+            ALOGV("Layers not scored");
+            return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                                  /*preferredDisplayModeOpt*/ std::nullopt),
+                    kNoSignals};
         } else {
-            return {bestRefreshRate, kNoSignals};
+            return {rankedRefreshRates, kNoSignals};
         }
     }
 
@@ -484,8 +586,6 @@
     // 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 DisplayModePtr& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
-
     const bool touchBoostForExplicitExact = [&] {
         if (mSupportsFrameRateOverrideByContent) {
             // Enable touch boost if there are other layers besides exact
@@ -496,15 +596,28 @@
         }
     }();
 
+    const auto& touchRefreshRates =
+            getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                          /*preferredDisplayModeOpt*/ std::nullopt);
     using fps_approx_ops::operator<;
 
     if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
-        bestRefreshRate->getFps() < touchRefreshRate->getFps()) {
-        ALOGV("TouchBoost - choose %s", to_string(touchRefreshRate->getFps()).c_str());
-        return {touchRefreshRate, GlobalSignals{.touch = true}};
+        scores.front().modeIt->second->getFps() <
+                touchRefreshRates.front().displayModePtr->getFps()) {
+        ALOGV("Touch Boost");
+        return {touchRefreshRates, GlobalSignals{.touch = true}};
     }
 
-    return {bestRefreshRate, kNoSignals};
+    // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
+    // current config
+    if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
+        const auto preferredDisplayMode = activeMode.getId();
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Ascending,
+                                              preferredDisplayMode),
+                kNoSignals};
+    }
+
+    return {rankedRefreshRates, kNoSignals};
 }
 
 std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
@@ -575,7 +688,7 @@
             continue;
         }
 
-        for (auto& [_, score] : scores) {
+        for (auto& [_, score, _1] : scores) {
             score = 0;
         }
 
@@ -587,7 +700,7 @@
             LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
                                 layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
                                 layer->vote != LayerVoteType::ExplicitExact);
-            for (auto& [modeIt, score] : scores) {
+            for (auto& [modeIt, score, _] : scores) {
                 constexpr bool isSeamlessSwitch = true;
                 const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(),
                                                                   isSeamlessSwitch);
@@ -605,15 +718,17 @@
 
         // If we never scored any layers, we don't have a preferred frame rate
         if (std::all_of(scores.begin(), scores.end(),
-                        [](RefreshRateScore score) { return score.score == 0; })) {
+                        [](RefreshRateScore score) { return score.overallScore == 0; })) {
             continue;
         }
 
-        // Now that we scored all the refresh rates we need to pick the one that got the highest
-        // score.
+        // Now that we scored all the refresh rates we need to pick the lowest refresh rate
+        // that got the highest score.
         const DisplayModePtr& bestRefreshRate =
-                getMaxScoreRefreshRate(scores.begin(), scores.end());
-
+                std::min_element(scores.begin(), scores.end(),
+                                 RefreshRateScoreComparator{.refreshRateOrder =
+                                                                    RefreshRateOrder::Ascending})
+                        ->modeIt->second;
         frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
     }
 
@@ -626,7 +741,7 @@
 
     const DisplayModePtr& current = desiredActiveModeId
             ? mDisplayModes.get(*desiredActiveModeId)->get()
-            : mActiveModeIt->second;
+            : getActiveModeItLocked()->second;
 
     const DisplayModePtr& min = mMinRefreshRateModeIt->second;
     if (current == min) {
@@ -638,26 +753,22 @@
 }
 
 const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+    const auto& activeMode = *getActiveModeItLocked()->second;
+
     for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
         const auto& mode = modeIt->second;
-        if (mActiveModeIt->second->getGroup() == mode->getGroup()) {
+        if (activeMode.getGroup() == mode->getGroup()) {
             return mode;
         }
     }
 
-    ALOGE("Can't find min refresh rate by policy with the same mode group"
-          " as the current mode %s",
-          to_string(*mActiveModeIt->second).c_str());
+    ALOGE("Can't find min refresh rate by policy with the same mode group as the current mode %s",
+          to_string(activeMode).c_str());
 
     // Default to the lowest refresh rate.
     return mPrimaryRefreshRates.front()->second;
 }
 
-DisplayModePtr RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
-    std::lock_guard lock(mLock);
-    return getMaxRefreshRateByPolicyLocked();
-}
-
 const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
     for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
         const auto& mode = (*it)->second;
@@ -666,25 +777,78 @@
         }
     }
 
-    ALOGE("Can't find max refresh rate by policy with the same mode group"
-          " as the current mode %s",
-          to_string(*mActiveModeIt->second).c_str());
+    ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
 
     // Default to the highest refresh rate.
     return mPrimaryRefreshRates.back()->second;
 }
 
-DisplayModePtr RefreshRateConfigs::getActiveMode() const {
+std::vector<RefreshRateRanking> RefreshRateConfigs::getRefreshRatesByPolicyLocked(
+        std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
+        std::optional<DisplayModeId> preferredDisplayModeOpt) const {
+    std::deque<RefreshRateRanking> rankings;
+    const auto makeRanking = [&](const DisplayModeIterator it) REQUIRES(mLock) {
+        const auto& mode = it->second;
+        if (anchorGroupOpt && mode->getGroup() != anchorGroupOpt) {
+            return;
+        }
+
+        float score = calculateRefreshRateScoreForFps(mode->getFps());
+        const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
+        if (inverseScore) {
+            score = 1.0f / score;
+        }
+        if (preferredDisplayModeOpt) {
+            if (*preferredDisplayModeOpt == mode->getId()) {
+                rankings.push_front(RefreshRateRanking{mode, /*score*/ 1.0f});
+                return;
+            }
+            constexpr float kNonPreferredModePenalty = 0.95f;
+            score *= kNonPreferredModePenalty;
+        }
+        rankings.push_back(RefreshRateRanking{mode, score});
+    };
+
+    if (refreshRateOrder == RefreshRateOrder::Ascending) {
+        std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), makeRanking);
+    } else {
+        std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), makeRanking);
+    }
+
+    if (!rankings.empty() || !anchorGroupOpt) {
+        return {rankings.begin(), rankings.end()};
+    }
+
+    ALOGW("Can't find %s refresh rate by policy with the same mode group"
+          " as the mode group %d",
+          refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
+
+    return getRefreshRatesByPolicyLocked(/*anchorGroupOpt*/ std::nullopt, refreshRateOrder,
+                                         preferredDisplayModeOpt);
+}
+
+DisplayModePtr RefreshRateConfigs::getActiveModePtr() const {
     std::lock_guard lock(mLock);
-    return mActiveModeIt->second;
+    return getActiveModeItLocked()->second;
+}
+
+const DisplayMode& RefreshRateConfigs::getActiveMode() const {
+    // Reads from kMainThreadContext do not require mLock.
+    ftl::FakeGuard guard(mLock);
+    return *mActiveModeIt->second;
+}
+
+DisplayModeIterator RefreshRateConfigs::getActiveModeItLocked() const {
+    // Reads under mLock do not require kMainThreadContext.
+    return FTL_FAKE_GUARD(kMainThreadContext, mActiveModeIt);
 }
 
 void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
 
-    // Invalidate the cached invocation to getBestRefreshRate. This forces
-    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
-    mGetBestRefreshRateCache.reset();
+    // Invalidate the cached invocation to getRankedRefreshRates. This forces
+    // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
+    mGetRankedRefreshRatesCache.reset();
 
     mActiveModeIt = mDisplayModes.find(modeId);
     LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
@@ -694,7 +858,7 @@
                                        Config config)
       : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
     initializeIdleTimer();
-    updateDisplayModes(std::move(modes), activeModeId);
+    FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId));
 }
 
 void RefreshRateConfigs::initializeIdleTimer() {
@@ -719,9 +883,9 @@
 void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
     std::lock_guard lock(mLock);
 
-    // Invalidate the cached invocation to getBestRefreshRate. This forces
-    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
-    mGetBestRefreshRateCache.reset();
+    // Invalidate the cached invocation to getRankedRefreshRates. This forces
+    // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
+    mGetRankedRefreshRatesCache.reset();
 
     mDisplayModes = std::move(modes);
     mActiveModeIt = mDisplayModes.find(activeModeId);
@@ -759,35 +923,60 @@
             policy.appRequestRange.max >= policy.primaryRange.max;
 }
 
-status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
-    std::lock_guard lock(mLock);
-    if (!isPolicyValidLocked(policy)) {
-        ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
-        return BAD_VALUE;
-    }
-    mGetBestRefreshRateCache.reset();
-    Policy previousPolicy = *getCurrentPolicyLocked();
-    mDisplayManagerPolicy = policy;
-    if (*getCurrentPolicyLocked() == previousPolicy) {
-        return CURRENT_POLICY_UNCHANGED;
-    }
-    constructAvailableRefreshRates();
-    return NO_ERROR;
-}
+auto RefreshRateConfigs::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
+    Policy oldPolicy;
+    {
+        std::lock_guard lock(mLock);
+        oldPolicy = *getCurrentPolicyLocked();
 
-status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
-    std::lock_guard lock(mLock);
-    if (policy && !isPolicyValidLocked(*policy)) {
-        return BAD_VALUE;
+        const bool valid = ftl::match(
+                policy,
+                [this](const auto& policy) {
+                    ftl::FakeGuard guard(mLock);
+                    if (!isPolicyValidLocked(policy)) {
+                        ALOGE("Invalid policy: %s", policy.toString().c_str());
+                        return false;
+                    }
+
+                    using T = std::decay_t<decltype(policy)>;
+
+                    if constexpr (std::is_same_v<T, DisplayManagerPolicy>) {
+                        mDisplayManagerPolicy = policy;
+                    } else {
+                        static_assert(std::is_same_v<T, OverridePolicy>);
+                        mOverridePolicy = policy;
+                    }
+                    return true;
+                },
+                [this](NoOverridePolicy) {
+                    ftl::FakeGuard guard(mLock);
+                    mOverridePolicy.reset();
+                    return true;
+                });
+
+        if (!valid) {
+            return SetPolicyResult::Invalid;
+        }
+
+        mGetRankedRefreshRatesCache.reset();
+
+        if (*getCurrentPolicyLocked() == oldPolicy) {
+            return SetPolicyResult::Unchanged;
+        }
+        constructAvailableRefreshRates();
     }
-    mGetBestRefreshRateCache.reset();
-    Policy previousPolicy = *getCurrentPolicyLocked();
-    mOverridePolicy = policy;
-    if (*getCurrentPolicyLocked() == previousPolicy) {
-        return CURRENT_POLICY_UNCHANGED;
-    }
-    constructAvailableRefreshRates();
-    return NO_ERROR;
+
+    const auto displayId = getActiveMode().getPhysicalDisplayId();
+    const unsigned numModeChanges = std::exchange(mNumModeSwitchesInPolicy, 0u);
+
+    ALOGI("Display %s policy changed\n"
+          "Previous: %s\n"
+          "Current:  %s\n"
+          "%u mode changes were performed under the previous policy",
+          to_string(displayId).c_str(), oldPolicy.toString().c_str(), toString(policy).c_str(),
+          numModeChanges);
+
+    return SetPolicyResult::Changed;
 }
 
 const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const {
@@ -880,7 +1069,8 @@
         return KernelIdleTimerAction::TurnOff;
     }
 
-    const DisplayModePtr& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+    const DisplayModePtr& maxByPolicy =
+            getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
     if (minByPolicy == maxByPolicy) {
         // Turn on the timer when the min of the primary range is below the device min.
         if (const Policy* currentPolicy = getCurrentPolicyLocked();
@@ -921,47 +1111,45 @@
             isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
 }
 
-void RefreshRateConfigs::dump(std::string& result) const {
-    using namespace std::string_literals;
+void RefreshRateConfigs::dump(utils::Dumper& dumper) const {
+    using namespace std::string_view_literals;
 
     std::lock_guard lock(mLock);
 
-    const auto activeModeId = mActiveModeIt->first;
-    result += "   activeModeId="s;
-    result += std::to_string(activeModeId.value());
+    const auto activeModeId = getActiveModeItLocked()->first;
+    dumper.dump("activeModeId"sv, std::to_string(activeModeId.value()));
 
-    result += "\n   displayModes=\n"s;
-    for (const auto& [id, mode] : mDisplayModes) {
-        result += "      "s;
-        result += to_string(*mode);
-        result += '\n';
+    dumper.dump("displayModes"sv);
+    {
+        utils::Dumper::Indent indent(dumper);
+        for (const auto& [id, mode] : mDisplayModes) {
+            dumper.dump({}, to_string(*mode));
+        }
     }
 
-    base::StringAppendF(&result, "   displayManagerPolicy=%s\n",
-                        mDisplayManagerPolicy.toString().c_str());
+    dumper.dump("displayManagerPolicy"sv, mDisplayManagerPolicy.toString());
 
     if (const Policy& currentPolicy = *getCurrentPolicyLocked();
         mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
-        base::StringAppendF(&result, "   overridePolicy=%s\n", currentPolicy.toString().c_str());
+        dumper.dump("overridePolicy"sv, currentPolicy.toString());
     }
 
-    base::StringAppendF(&result, "   supportsFrameRateOverrideByContent=%s\n",
-                        mSupportsFrameRateOverrideByContent ? "true" : "false");
+    dumper.dump("supportsFrameRateOverrideByContent"sv, mSupportsFrameRateOverrideByContent);
 
-    result += "   idleTimer="s;
+    std::string idleTimer;
     if (mIdleTimer) {
-        result += mIdleTimer->dump();
+        idleTimer = mIdleTimer->dump();
     } else {
-        result += "off"s;
+        idleTimer = "off"sv;
     }
 
     if (const auto controller = mConfig.kernelIdleTimerController) {
-        base::StringAppendF(&result, " (kernel via %s)", ftl::enum_string(*controller).c_str());
+        base::StringAppendF(&idleTimer, " (kernel via %s)", ftl::enum_string(*controller).c_str());
     } else {
-        result += " (platform)"s;
+        idleTimer += " (platform)"sv;
     }
 
-    result += '\n';
+    dumper.dump("idleTimer"sv, idleTimer);
 }
 
 std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 05a8692..7219584 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -21,6 +21,7 @@
 #include <optional>
 #include <type_traits>
 #include <utility>
+#include <variant>
 
 #include <gui/DisplayEventReceiver.h>
 
@@ -31,6 +32,8 @@
 #include "DisplayHardware/HWComposer.h"
 #include "Scheduler/OneShotTimer.h"
 #include "Scheduler/StrongTyping.h"
+#include "ThreadContext.h"
+#include "Utils/Dumper.h"
 
 namespace android::scheduler {
 
@@ -43,6 +46,15 @@
     return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
 }
 
+struct RefreshRateRanking {
+    DisplayModePtr displayModePtr;
+    float score = 0.0f;
+
+    bool operator==(const RefreshRateRanking& ranking) const {
+        return displayModePtr == ranking.displayModePtr && score == ranking.score;
+    }
+};
+
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
 
 /**
@@ -56,8 +68,7 @@
     static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
             std::chrono::nanoseconds(800us).count();
 
-    struct Policy {
-    private:
+    class Policy {
         static constexpr int kAllowGroupSwitchingDefault = false;
 
     public:
@@ -107,23 +118,28 @@
         std::string toString() const;
     };
 
-    // Return code set*Policy() to indicate the current policy is unchanged.
-    static constexpr int CURRENT_POLICY_UNCHANGED = 1;
+    enum class SetPolicyResult { Invalid, Unchanged, Changed };
 
     // We maintain the display manager policy and the override policy separately. The override
     // policy is used by CTS tests to get a consistent device state for testing. While the override
     // policy is set, it takes precedence over the display manager policy. Once the override policy
     // is cleared, we revert to using the display manager policy.
+    struct DisplayManagerPolicy : Policy {
+        using Policy::Policy;
+    };
 
-    // Sets the display manager policy to choose refresh rates. The return value will be:
-    //   - A negative value if the policy is invalid or another error occurred.
-    //   - NO_ERROR if the policy was successfully updated, and the current policy is different from
-    //     what it was before the call.
-    //   - CURRENT_POLICY_UNCHANGED if the policy was successfully updated, but the current policy
-    //     is the same as it was before the call.
-    status_t setDisplayManagerPolicy(const Policy& policy) EXCLUDES(mLock);
-    // Sets the override policy. See setDisplayManagerPolicy() for the meaning of the return value.
-    status_t setOverridePolicy(const std::optional<Policy>& policy) EXCLUDES(mLock);
+    struct OverridePolicy : Policy {
+        using Policy::Policy;
+    };
+
+    struct NoOverridePolicy {};
+
+    using PolicyVariant = std::variant<DisplayManagerPolicy, OverridePolicy, NoOverridePolicy>;
+
+    SetPolicyResult setPolicy(const PolicyVariant&) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+
+    void onModeChangeInitiated() REQUIRES(kMainThreadContext) { mNumModeSwitchesInPolicy++; }
+
     // Gets the current policy, which will be the override policy if active, and the display manager
     // policy otherwise.
     Policy getCurrentPolicy() const EXCLUDES(mLock);
@@ -184,15 +200,19 @@
         bool touch = false;
         // True if the system hasn't seen any buffers posted to layers recently.
         bool idle = false;
+        // Whether the display is about to be powered on, or has been in PowerMode::ON
+        // within the timeout of DisplayPowerTimer.
+        bool powerOnImminent = false;
 
         bool operator==(GlobalSignals other) const {
-            return touch == other.touch && idle == other.idle;
+            return touch == other.touch && idle == other.idle &&
+                    powerOnImminent == other.powerOnImminent;
         }
     };
 
-    // Returns the refresh rate that best fits the given layers, and whether the refresh rate was
-    // chosen based on touch boost and/or idle timer.
-    std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRate(
+    // Returns the list in the descending order of refresh rates desired
+    // based on their overall score, and the GlobalSignals that were considered.
+    std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedRefreshRates(
             const std::vector<LayerRequirement>&, GlobalSignals) const EXCLUDES(mLock);
 
     FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
@@ -203,12 +223,11 @@
     std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
                                             bool timerExpired) const EXCLUDES(mLock);
 
-    // Returns the highest refresh rate according to the current policy. May change at runtime. Only
-    // uses the primary range, not the app request range.
-    DisplayModePtr getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+    void setActiveModeId(DisplayModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
 
-    void setActiveModeId(DisplayModeId) EXCLUDES(mLock);
-    DisplayModePtr getActiveMode() const EXCLUDES(mLock);
+    // See mActiveModeIt for thread safety.
+    DisplayModePtr getActiveModePtr() const EXCLUDES(mLock);
+    const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext);
 
     // Returns a known frame rate that is the closest to frameRate
     Fps findClosestKnownFrameRate(Fps frameRate) const;
@@ -323,7 +342,7 @@
         mIdleTimer->reset();
     }
 
-    void dump(std::string& result) const EXCLUDES(mLock);
+    void dump(utils::Dumper&) const EXCLUDES(mLock);
 
     std::chrono::milliseconds getIdleTimerTimeout();
 
@@ -332,7 +351,10 @@
 
     void constructAvailableRefreshRates() REQUIRES(mLock);
 
-    std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRateLocked(
+    // See mActiveModeIt for thread safety.
+    DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
+
+    std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedRefreshRatesLocked(
             const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock);
 
     // Returns number of display frames and remainder when dividing the layer refresh period by
@@ -346,16 +368,22 @@
     // 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 DisplayModePtr& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
-    const DisplayModePtr& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
-        return getMaxRefreshRateByPolicyLocked(mActiveModeIt->second->getGroup());
-    }
+
+    struct RefreshRateScoreComparator;
+
+    enum class RefreshRateOrder { Ascending, Descending };
+
+    // Returns the rankings in RefreshRateOrder. May change at runtime.
+    // Only uses the primary range, not the app request range.
+    std::vector<RefreshRateRanking> getRefreshRatesByPolicyLocked(
+            std::optional<int> anchorGroupOpt, RefreshRateOrder,
+            std::optional<DisplayModeId> preferredDisplayModeOpt) const REQUIRES(mLock);
 
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
-    // Returns whether the layer is allowed to vote for the given refresh rate.
-    bool isVoteAllowed(const LayerRequirement&, Fps) const;
-
+    // Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1.
+    float calculateRefreshRateScoreForFps(Fps refreshRate) const REQUIRES(mLock);
     // calculates a score for a layer. Used to determine the display refresh rate
     // and the frame rate override for certains applications.
     float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
@@ -364,7 +392,8 @@
     float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const
             REQUIRES(mLock);
 
-    void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock);
+    void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock)
+            REQUIRES(kMainThreadContext);
 
     void initializeIdleTimer();
 
@@ -380,7 +409,10 @@
     // is also dependent, so must be reset as well.
     DisplayModes mDisplayModes GUARDED_BY(mLock);
 
-    DisplayModeIterator mActiveModeIt GUARDED_BY(mLock);
+    // Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext
+    // need not be under mLock.
+    DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext);
+
     DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock);
     DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
 
@@ -391,6 +423,8 @@
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
 
+    unsigned mNumModeSwitchesInPolicy GUARDED_BY(kMainThreadContext) = 0;
+
     mutable std::mutex mLock;
 
     // A sorted list of known frame rates that a Heuristic layer will choose
@@ -400,11 +434,11 @@
     const Config mConfig;
     bool mSupportsFrameRateOverrideByContent;
 
-    struct GetBestRefreshRateCache {
+    struct GetRankedRefreshRatesCache {
         std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
-        std::pair<DisplayModePtr, GlobalSignals> result;
+        std::pair<std::vector<RefreshRateRanking>, GlobalSignals> result;
     };
-    mutable std::optional<GetBestRefreshRateCache> mGetBestRefreshRateCache GUARDED_BY(mLock);
+    mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock);
 
     // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
     std::mutex mIdleTimerCallbacksMutex;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 55ae013..12949d6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -43,7 +43,6 @@
 #include "DispSyncSource.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
-#include "InjectVSyncSource.h"
 #include "OneShotTimer.h"
 #include "SurfaceFlingerProperties.h"
 #include "VSyncPredictor.h"
@@ -57,6 +56,39 @@
         }                                                            \
     } while (false)
 
+namespace {
+
+using android::Fps;
+using android::FpsApproxEqual;
+using android::FpsHash;
+using android::scheduler::AggregatedFpsScore;
+using android::scheduler::RefreshRateRankingsAndSignals;
+
+// Returns the aggregated score per Fps for the RefreshRateRankingsAndSignals sourced.
+auto getAggregatedScoresPerFps(
+        const std::vector<RefreshRateRankingsAndSignals>& refreshRateRankingsAndSignalsPerDisplay)
+        -> std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> {
+    std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> aggregatedScoresPerFps;
+
+    for (const auto& refreshRateRankingsAndSignal : refreshRateRankingsAndSignalsPerDisplay) {
+        const auto& refreshRateRankings = refreshRateRankingsAndSignal.refreshRateRankings;
+
+        std::for_each(refreshRateRankings.begin(), refreshRateRankings.end(), [&](const auto& it) {
+            const auto [score, result] =
+                    aggregatedScoresPerFps.try_emplace(it.displayModePtr->getFps(),
+                                                       AggregatedFpsScore{it.score,
+                                                                          /* numDisplays */ 1});
+            if (!result) { // update
+                score->second.totalScore += it.score;
+                score->second.numDisplays++;
+            }
+        });
+    }
+    return aggregatedScoresPerFps;
+}
+
+} // namespace
+
 namespace android::scheduler {
 
 Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
@@ -182,7 +214,7 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = holdRefreshRateConfigs()->getActiveMode()->getFps();
+        const Fps refreshRate = holdRefreshRateConfigs()->getActiveModePtr()->getFps();
         const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
 
         const auto frameRate = getFrameRateOverride(uid);
@@ -198,15 +230,14 @@
     };
 }
 
-ConnectionHandle Scheduler::createConnection(
-        const char* connectionName, frametimeline::TokenManager* tokenManager,
-        std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
-        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+ConnectionHandle Scheduler::createConnection(const char* connectionName,
+                                             frametimeline::TokenManager* tokenManager,
+                                             std::chrono::nanoseconds workDuration,
+                                             std::chrono::nanoseconds readyDuration) {
     auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
     auto throttleVsync = makeThrottleVsyncCallback();
     auto getVsyncPeriod = makeGetVsyncPeriodFunction();
     auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
-                                                           std::move(interceptCallback),
                                                            std::move(throttleVsync),
                                                            std::move(getVsyncPeriod));
     return createConnection(std::move(eventThread));
@@ -320,7 +351,7 @@
     // 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->getActiveMode() != mPolicy.mode) {
+        mRefreshRateConfigs->getActiveModePtr() != mPolicy.mode) {
         return;
     }
 
@@ -371,44 +402,6 @@
     thread->setDuration(workDuration, readyDuration);
 }
 
-ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
-    if (mInjectVSyncs == enable) {
-        return {};
-    }
-
-    ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling");
-
-    if (!mInjectorConnectionHandle) {
-        auto vsyncSource = std::make_unique<InjectVSyncSource>();
-        mVSyncInjector = vsyncSource.get();
-
-        auto eventThread =
-                std::make_unique<impl::EventThread>(std::move(vsyncSource),
-                                                    /*tokenManager=*/nullptr,
-                                                    impl::EventThread::InterceptVSyncsCallback(),
-                                                    impl::EventThread::ThrottleVsyncCallback(),
-                                                    impl::EventThread::GetVsyncPeriodFunction());
-
-        // EventThread does not dispatch VSYNC unless the display is connected and powered on.
-        eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true);
-        eventThread->onScreenAcquired();
-
-        mInjectorConnectionHandle = createConnection(std::move(eventThread));
-    }
-
-    mInjectVSyncs = enable;
-    return mInjectorConnectionHandle;
-}
-
-bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp) {
-    if (!mInjectVSyncs || !mVSyncInjector) {
-        return false;
-    }
-
-    mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime, deadlineTimestamp);
-    return true;
-}
-
 void Scheduler::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
@@ -453,7 +446,7 @@
     if (now - last > kIgnoreDelay) {
         const auto refreshRate = [&] {
             std::scoped_lock lock(mRefreshRateConfigsLock);
-            return mRefreshRateConfigs->getActiveMode()->getFps();
+            return mRefreshRateConfigs->getActiveModePtr()->getFps();
         }();
         resyncToHardwareVsync(false, refreshRate);
     }
@@ -577,7 +570,7 @@
     // magic number
     const Fps refreshRate = [&] {
         std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getActiveMode()->getFps();
+        return mRefreshRateConfigs->getActiveModePtr()->getFps();
     }();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
@@ -662,6 +655,7 @@
 auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
     DisplayModePtr newMode;
     GlobalSignals consideredSignals;
+    std::vector<DisplayModeConfig> displayModeConfigs;
 
     bool refreshRateChanged = false;
     bool frameRateOverridesChanged;
@@ -674,7 +668,27 @@
         if (currentState == newState) return {};
         currentState = std::forward<T>(newState);
 
-        std::tie(newMode, consideredSignals) = chooseDisplayMode();
+        displayModeConfigs = getBestDisplayModeConfigs();
+
+        // mPolicy holds the current mode, using the current mode we find out
+        // what display is currently being tracked through the policy and
+        // then find the DisplayModeConfig for that display. So that
+        // later we check if the policy mode has changed for the same display in policy.
+        // If mPolicy mode isn't available then we take the first display from the best display
+        // modes as the candidate for policy changes and frame rate overrides.
+        // TODO(b/240743786) Update the single display based assumptions and make mode changes
+        // and mPolicy per display.
+        const DisplayModeConfig& displayModeConfigForCurrentPolicy = mPolicy.mode
+                ? *std::find_if(displayModeConfigs.begin(), displayModeConfigs.end(),
+                                [&](const auto& displayModeConfig) REQUIRES(mPolicyLock) {
+                                    return displayModeConfig.displayModePtr
+                                                   ->getPhysicalDisplayId() ==
+                                            mPolicy.mode->getPhysicalDisplayId();
+                                })
+                : displayModeConfigs.front();
+
+        newMode = displayModeConfigForCurrentPolicy.displayModePtr;
+        consideredSignals = displayModeConfigForCurrentPolicy.signals;
         frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
 
         if (mPolicy.mode == newMode) {
@@ -689,9 +703,7 @@
         }
     }
     if (refreshRateChanged) {
-        mSchedulerCallback.requestDisplayMode(std::move(newMode),
-                                              consideredSignals.idle ? DisplayModeEvent::None
-                                                                     : DisplayModeEvent::Changed);
+        mSchedulerCallback.requestDisplayModes(std::move(displayModeConfigs));
     }
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -699,31 +711,89 @@
     return consideredSignals;
 }
 
-auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> {
+void Scheduler::registerDisplay(const sp<const DisplayDevice>& display) {
+    const bool ok = mDisplays.try_emplace(display->getPhysicalId(), display).second;
+    ALOGE_IF(!ok, "Duplicate display registered");
+}
+
+void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+    mDisplays.erase(displayId);
+}
+
+std::vector<DisplayModeConfig> Scheduler::getBestDisplayModeConfigs() const {
     ATRACE_CALL();
 
-    const auto configs = holdRefreshRateConfigs();
+    std::vector<RefreshRateRankingsAndSignals> refreshRateRankingsAndSignalsPerDisplay;
+    refreshRateRankingsAndSignalsPerDisplay.reserve(mDisplays.size());
 
-    // 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 &&
-        (mPolicy.displayPowerMode != hal::PowerMode::ON ||
-         mPolicy.displayPowerTimer == TimerState::Reset)) {
-        constexpr GlobalSignals kNoSignals;
-        return {configs->getMaxRefreshRateByPolicy(), kNoSignals};
+    for (const auto& [id, display] : mDisplays) {
+        const auto [rankings, signals] =
+                display->holdRefreshRateConfigs()
+                        ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals());
+
+        refreshRateRankingsAndSignalsPerDisplay.emplace_back(
+                RefreshRateRankingsAndSignals{rankings, signals});
     }
 
-    const GlobalSignals signals{.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
-                                .idle = mPolicy.idleTimer == TimerState::Expired};
+    // FPS and their Aggregated score.
+    std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> aggregatedScoresPerFps =
+            getAggregatedScoresPerFps(refreshRateRankingsAndSignalsPerDisplay);
 
-    return configs->getBestRefreshRate(mPolicy.contentRequirements, signals);
+    auto maxScoreIt = aggregatedScoresPerFps.cbegin();
+    // Selects the max Fps that is present on all the displays.
+    for (auto it = aggregatedScoresPerFps.cbegin(); it != aggregatedScoresPerFps.cend(); ++it) {
+        const auto [fps, aggregatedScore] = *it;
+        if (aggregatedScore.numDisplays == mDisplays.size() &&
+            aggregatedScore.totalScore >= maxScoreIt->second.totalScore) {
+            maxScoreIt = it;
+        }
+    }
+    return getDisplayModeConfigsForTheChosenFps(maxScoreIt->first,
+                                                refreshRateRankingsAndSignalsPerDisplay);
+}
+
+std::vector<DisplayModeConfig> Scheduler::getDisplayModeConfigsForTheChosenFps(
+        Fps chosenFps,
+        const std::vector<RefreshRateRankingsAndSignals>& refreshRateRankingsAndSignalsPerDisplay)
+        const {
+    std::vector<DisplayModeConfig> displayModeConfigs;
+    displayModeConfigs.reserve(mDisplays.size());
+    using fps_approx_ops::operator==;
+    std::for_each(refreshRateRankingsAndSignalsPerDisplay.begin(),
+                  refreshRateRankingsAndSignalsPerDisplay.end(),
+                  [&](const auto& refreshRateRankingsAndSignal) {
+                      for (const auto& ranking : refreshRateRankingsAndSignal.refreshRateRankings) {
+                          if (ranking.displayModePtr->getFps() == chosenFps) {
+                              displayModeConfigs.emplace_back(
+                                      DisplayModeConfig{refreshRateRankingsAndSignal.globalSignals,
+                                                        ranking.displayModePtr});
+                              break;
+                          }
+                      }
+                  });
+    return displayModeConfigs;
+}
+
+GlobalSignals Scheduler::makeGlobalSignals() const {
+    const bool powerOnImminent = mDisplayPowerTimer &&
+            (mPolicy.displayPowerMode != hal::PowerMode::ON ||
+             mPolicy.displayPowerTimer == TimerState::Reset);
+
+    return {.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
+            .idle = mPolicy.idleTimer == TimerState::Expired,
+            .powerOnImminent = powerOnImminent};
 }
 
 DisplayModePtr Scheduler::getPreferredDisplayMode() {
     std::lock_guard<std::mutex> lock(mPolicyLock);
     // Make sure the stored mode is up to date.
     if (mPolicy.mode) {
-        mPolicy.mode = chooseDisplayMode().first;
+        const auto configs = holdRefreshRateConfigs();
+        const auto rankings =
+                configs->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
+                        .first;
+
+        mPolicy.mode = rankings.front().displayModePtr;
     }
     return mPolicy.mode;
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4730493..25fa714 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -24,6 +24,7 @@
 #include <mutex>
 #include <optional>
 #include <unordered_map>
+#include <utility>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -32,9 +33,11 @@
 #include <ui/GraphicTypes.h>
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
+#include <DisplayDevice.h>
 #include <scheduler/Features.h>
 #include <scheduler/Time.h>
 
+#include "Display/DisplayMap.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
 #include "LayerHistory.h"
@@ -75,7 +78,6 @@
 namespace android {
 
 class FenceTime;
-class InjectVSyncSource;
 
 namespace frametimeline {
 class TokenManager;
@@ -83,11 +85,22 @@
 
 namespace scheduler {
 
+using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+// Config representing the DisplayMode and considered signals for the Display.
+struct DisplayModeConfig {
+    const GlobalSignals signals;
+    const DisplayModePtr displayModePtr;
+
+    DisplayModeConfig(GlobalSignals signals, DisplayModePtr displayModePtr)
+          : signals(signals), displayModePtr(std::move(displayModePtr)) {}
+};
+
 struct ISchedulerCallback {
     using DisplayModeEvent = scheduler::DisplayModeEvent;
 
     virtual void setVsyncEnabled(bool) = 0;
-    virtual void requestDisplayMode(DisplayModePtr, DisplayModeEvent) = 0;
+    virtual void requestDisplayModes(std::vector<DisplayModeConfig>) = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
     virtual void triggerOnFrameRateOverridesChanged() = 0;
 
@@ -95,8 +108,21 @@
     ~ISchedulerCallback() = default;
 };
 
-class Scheduler : impl::MessageQueue {
-    using Impl = impl::MessageQueue;
+// Holds the total score of the FPS and
+// number of displays the FPS is found in.
+struct AggregatedFpsScore {
+    float totalScore;
+    size_t numDisplays;
+};
+
+// Represents the RefreshRateRankings and GlobalSignals for the selected RefreshRateRankings.
+struct RefreshRateRankingsAndSignals {
+    std::vector<RefreshRateRanking> refreshRateRankings;
+    GlobalSignals globalSignals;
+};
+
+class Scheduler : android::impl::MessageQueue {
+    using Impl = android::impl::MessageQueue;
 
 public:
     Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
@@ -111,11 +137,11 @@
     void createVsyncSchedule(FeatureFlags);
 
     using Impl::initVsync;
-    using Impl::setInjector;
 
     using Impl::getScheduledFrameTime;
     using Impl::setDuration;
 
+    using Impl::scheduleConfigure;
     using Impl::scheduleFrame;
 
     // Schedule an asynchronous or synchronous task on the main thread.
@@ -128,8 +154,7 @@
 
     ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
                                       std::chrono::nanoseconds workDuration,
-                                      std::chrono::nanoseconds readyDuration,
-                                      impl::EventThread::InterceptVSyncsCallback);
+                                      std::chrono::nanoseconds readyDuration);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(
             ConnectionHandle, EventRegistrationFlags eventRegistration = {});
@@ -149,10 +174,6 @@
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
-    // Returns injector handle if injection has toggled, or an invalid handle otherwise.
-    ConnectionHandle enableVSyncInjection(bool enable);
-    // Returns false if injection is disabled.
-    bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp);
     void enableHardwareVsync();
     void disableHardwareVsync(bool makeUnavailable);
 
@@ -228,7 +249,7 @@
 
     nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
         std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getActiveMode()->getFps().getPeriodNsecs();
+        return mRefreshRateConfigs->getActiveModePtr()->getFps().getPeriodNsecs();
     }
 
     // Returns the framerate of the layer with the given sequence ID
@@ -236,6 +257,9 @@
         return mLayerHistory.getLayerFramerate(now, id);
     }
 
+    void registerDisplay(const sp<const DisplayDevice>&);
+    void unregisterDisplay(PhysicalDisplayId);
+
 private:
     friend class TestableScheduler;
 
@@ -259,8 +283,6 @@
 
     void setVsyncPeriod(nsecs_t period);
 
-    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
-
     struct Policy;
 
     // Sets the S state of the policy to the T value under mPolicyLock, and chooses a display mode
@@ -268,16 +290,22 @@
     template <typename S, typename T>
     GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock);
 
-    // Returns the display mode that fulfills the policy, and the signals that were considered.
-    std::pair<DisplayModePtr, GlobalSignals> chooseDisplayMode() REQUIRES(mPolicyLock);
+    // Returns the best display mode per display.
+    std::vector<DisplayModeConfig> getBestDisplayModeConfigs() const REQUIRES(mPolicyLock);
+
+    // Returns the list of DisplayModeConfigs per display for the chosenFps.
+    std::vector<DisplayModeConfig> getDisplayModeConfigsForTheChosenFps(
+            Fps chosenFps, const std::vector<RefreshRateRankingsAndSignals>&) const;
+
+    GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
 
     bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
 
     void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
 
-    impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
+    android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
             EXCLUDES(mRefreshRateConfigsLock);
-    impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
+    android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
     std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const
             EXCLUDES(mRefreshRateConfigsLock) {
@@ -295,10 +323,6 @@
     mutable std::mutex mConnectionsLock;
     std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
 
-    bool mInjectVSyncs = false;
-    InjectVSyncSource* mVSyncInjector = nullptr;
-    ConnectionHandle mInjectorConnectionHandle;
-
     mutable std::mutex mHWVsyncLock;
     bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
     bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
@@ -320,6 +344,10 @@
 
     mutable std::mutex mPolicyLock;
 
+    // Holds the Physical displays registered through the SurfaceFlinger, used for making
+    // the refresh rate selections.
+    display::PhysicalDisplayMap<PhysicalDisplayId, const sp<const DisplayDevice>> mDisplays;
+
     struct Policy {
         // Policy for choosing the display mode.
         LayerHistory::Summary contentRequirements;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 77782e9..898e865 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -167,7 +167,9 @@
         vsyncTS[i] = timestamp;
         meanTS += timestamp;
 
-        const auto ordinal = (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor;
+        const auto ordinal = currentPeriod == 0
+                ? 0
+                : (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor;
         ordinals[i] = ordinal;
         meanOrdinal += ordinal;
     }
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 13cd304..e23945d 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -19,6 +19,7 @@
 #define LOG_TAG "VSyncReactor"
 //#define LOG_NDEBUG 0
 
+#include <assert.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 #include <utils/Trace.h>
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index bd4f409..2c77142 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -138,6 +138,10 @@
     bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
 };
 
+struct FpsHash {
+    size_t operator()(Fps fps) const { return std::hash<nsecs_t>()(fps.getPeriodNsecs()); }
+};
+
 inline std::string to_string(Fps fps) {
     return base::StringPrintf("%.2f Hz", fps.getValue());
 }
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
index f00d456..2ca55d4 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Time.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
@@ -41,6 +41,8 @@
 
     static constexpr TimePoint fromNs(nsecs_t);
 
+    static TimePoint now() { return scheduler::SchedulerClock::now(); };
+
     nsecs_t ns() const;
 };
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 744cb46..e4fee1c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -53,9 +53,10 @@
 #include <configstore/Utils.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
+#include <ftl/algorithm.h>
 #include <ftl/fake_guard.h>
 #include <ftl/future.h>
-#include <ftl/small_map.h>
+#include <ftl/unit.h>
 #include <gui/AidlStatusUtil.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
@@ -105,9 +106,9 @@
 
 #include <ui/DisplayIdentification.h>
 #include "BackgroundExecutor.h"
-#include "BufferStateLayer.h"
 #include "Client.h"
 #include "Colorizer.h"
+#include "Display/DisplayMap.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/FramebufferSurface.h"
@@ -116,7 +117,6 @@
 #include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
 #include "DisplayRenderArea.h"
-#include "EffectLayer.h"
 #include "Effects/Daltonizer.h"
 #include "FlagManager.h"
 #include "FpsReporter.h"
@@ -129,19 +129,16 @@
 #include "LayerVector.h"
 #include "MutexUtils.h"
 #include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
-#include "Scheduler/DispSyncSource.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
-#include "Scheduler/VsyncController.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerProperties.h"
-#include "SurfaceInterceptor.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
+#include "Utils/Dumper.h"
 #include "WindowInfosListenerInvoker.h"
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
@@ -172,6 +169,8 @@
         OutputCompositionState::CompositionStrategyPredictionState;
 
 using base::StringAppendF;
+using display::PhysicalDisplay;
+using display::PhysicalDisplays;
 using gui::DisplayInfo;
 using gui::GameMode;
 using gui::IDisplayEventConnection;
@@ -179,7 +178,6 @@
 using gui::LayerMetadata;
 using gui::WindowInfo;
 using gui::aidl_utils::binderStatusFromStatusT;
-using ui::ColorMode;
 using ui::Dataspace;
 using ui::DisplayPrimaries;
 using ui::RenderIntent;
@@ -190,33 +188,6 @@
 
 namespace {
 
-#pragma clang diagnostic push
-#pragma clang diagnostic error "-Wswitch-enum"
-
-bool isWideColorMode(const ColorMode colorMode) {
-    switch (colorMode) {
-        case ColorMode::DISPLAY_P3:
-        case ColorMode::ADOBE_RGB:
-        case ColorMode::DCI_P3:
-        case ColorMode::BT2020:
-        case ColorMode::DISPLAY_BT2020:
-        case ColorMode::BT2100_PQ:
-        case ColorMode::BT2100_HLG:
-            return true;
-        case ColorMode::NATIVE:
-        case ColorMode::STANDARD_BT601_625:
-        case ColorMode::STANDARD_BT601_625_UNADJUSTED:
-        case ColorMode::STANDARD_BT601_525:
-        case ColorMode::STANDARD_BT601_525_UNADJUSTED:
-        case ColorMode::STANDARD_BT709:
-        case ColorMode::SRGB:
-            return false;
-    }
-    return false;
-}
-
-#pragma clang diagnostic pop
-
 // TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
 constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
 
@@ -288,7 +259,6 @@
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
 uint32_t SurfaceFlinger::maxGraphicsWidth;
 uint32_t SurfaceFlinger::maxGraphicsHeight;
-bool SurfaceFlinger::hasWideColorDisplay;
 bool SurfaceFlinger::useContextPriority;
 Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
@@ -329,7 +299,6 @@
 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, mPid)),
@@ -357,11 +326,11 @@
     maxGraphicsWidth = std::max(max_graphics_width(0), 0);
     maxGraphicsHeight = std::max(max_graphics_height(0), 0);
 
-    hasWideColorDisplay = has_wide_color_display(false);
+    mSupportsWideColor = has_wide_color_display(false);
     mDefaultCompositionDataspace =
             static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
     mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace(
-            hasWideColorDisplay ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB));
+            mSupportsWideColor ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB));
     defaultCompositionDataspace = mDefaultCompositionDataspace;
     wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
     defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
@@ -524,7 +493,6 @@
     state.isSecure = secure;
     state.displayName = displayName;
     mCurrentState.displays.add(token, state);
-    mInterceptor->saveDisplayCreation(state);
     return token;
 }
 
@@ -542,7 +510,6 @@
         ALOGE("%s: Invalid operation on physical display", __func__);
         return;
     }
-    mInterceptor->saveDisplayDeletion(state.sequenceId);
     mCurrentState.displays.removeItemsAt(index);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -594,12 +561,12 @@
 
 std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
     std::vector<PhysicalDisplayId> displayIds;
-    displayIds.reserve(mPhysicalDisplayTokens.size());
+    displayIds.reserve(mPhysicalDisplays.size());
 
     const auto defaultDisplayId = getDefaultDisplayDeviceLocked()->getPhysicalId();
     displayIds.push_back(defaultDisplayId);
 
-    for (const auto& [id, token] : mPhysicalDisplayTokens) {
+    for (const auto& [id, display] : mPhysicalDisplays) {
         if (id != defaultDisplayId) {
             displayIds.push_back(id);
         }
@@ -608,6 +575,12 @@
     return displayIds;
 }
 
+std::optional<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdLocked(
+        const sp<display::DisplayToken>& displayToken) const {
+    return ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+            .transform(&ftl::to_key<PhysicalDisplays>);
+}
+
 sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
     Mutex::Autolock lock(mStateLock);
     return getPhysicalDisplayTokenLocked(displayId);
@@ -672,7 +645,7 @@
 
     sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
 
-    static_cast<void>(mScheduler->schedule([=] {
+    static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) {
         if (input == nullptr) {
             ALOGE("Failed to link to input service");
         } else {
@@ -681,26 +654,28 @@
 
         readPersistentProperties();
         mPowerAdvisor->onBootFinished();
-
-        // try to enable power hint session again using mendel flag now that boot is finished,
-        // but only if we didn't already try earlier
-        if (!mPowerAdvisor->usePowerHintSession() && mFlagManager.use_adpf_cpu_hint()) {
-            mPowerAdvisor->enablePowerHint(true);
-            // check again to make sure it's actually supported
-            if (mPowerAdvisor->usePowerHintSession()) {
-                startPowerHintSession();
+        const bool powerHintEnabled = mFlagManager.use_adpf_cpu_hint();
+        mPowerAdvisor->enablePowerHint(powerHintEnabled);
+        const bool powerHintUsed = mPowerAdvisor->usePowerHintSession();
+        ALOGD("Power hint is %s",
+              powerHintUsed ? "supported" : (powerHintEnabled ? "unsupported" : "disabled"));
+        if (powerHintUsed) {
+            std::optional<pid_t> renderEngineTid = getRenderEngine().getRenderEngineTid();
+            std::vector<int32_t> tidList;
+            tidList.emplace_back(gettid());
+            if (renderEngineTid.has_value()) {
+                tidList.emplace_back(*renderEngineTid);
+            }
+            if (!mPowerAdvisor->startPowerHintSession(tidList)) {
+                ALOGW("Cannot start power hint session");
             }
         }
 
-        ALOGD("Power hint session is %s",
-              mPowerAdvisor->usePowerHintSession()
-                      ? "enabled"
-                      : (!mPowerAdvisor->supportsPowerHintSession() ? "unsupported" : "disabled"));
-
         mBootStage = BootStage::FINISHED;
 
-        if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
-            FTL_FAKE_GUARD(mStateLock, enableRefreshRateOverlay(true));
+        if (base::GetBoolProperty("sf.debug.show_refresh_rate_overlay"s, false)) {
+            ftl::FakeGuard guard(mStateLock);
+            enableRefreshRateOverlay(true);
         }
     }));
 }
@@ -762,10 +737,11 @@
 
 // Do not call property_set on main thread which will be blocked by init
 // Use StartPropertySetThread instead.
-void SurfaceFlinger::init() {
+void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
-    Mutex::Autolock _l(mStateLock);
+    addTransactionReadyFilters();
+    Mutex::Autolock lock(mStateLock);
 
     // 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.
@@ -804,13 +780,36 @@
         enableHalVirtualDisplays(true);
     }
 
-    // Process any initial hotplug and resulting display changes.
-    processDisplayHotplugEventsLocked();
-    const auto display = getDefaultDisplayDeviceLocked();
-    LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback.");
-    const auto displayId = display->getPhysicalId();
-    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
-                        "Primary display is disconnected.");
+    // Process hotplug for displays connected at boot.
+    LOG_ALWAYS_FATAL_IF(!configureLocked(),
+                        "Initial display configuration failed: HWC did not hotplug");
+
+    // Commit primary display.
+    sp<const DisplayDevice> display;
+    if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) {
+        const auto& displays = mCurrentState.displays;
+
+        const auto& token = displays.keyAt(*indexOpt);
+        const auto& state = displays.valueAt(*indexOpt);
+
+        processDisplayAdded(token, state);
+        mDrawingState.displays.add(token, state);
+
+        display = getDefaultDisplayDeviceLocked();
+    }
+
+    LOG_ALWAYS_FATAL_IF(!display, "Failed to configure the primary display");
+    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(display->getPhysicalId()),
+                        "Primary display is disconnected");
+
+    // TODO(b/241285876): The Scheduler needlessly depends on creating the CompositionEngine part of
+    // the DisplayDevice, hence the above commit of the primary display. Remove that special case by
+    // initializing the Scheduler after configureLocked, once decoupled from DisplayDevice.
+    initScheduler(display);
+    dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
+
+    // Commit secondary display(s).
+    processDisplayChangesLocked();
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
@@ -820,11 +819,6 @@
 
     mPowerAdvisor->init();
 
-    mPowerAdvisor->enablePowerHint(mFlagManager.use_adpf_cpu_hint());
-    if (mPowerAdvisor->usePowerHintSession()) {
-        startPowerHintSession();
-    }
-
     char primeShaderCache[PROPERTY_VALUE_MAX];
     property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
     if (atoi(primeShaderCache)) {
@@ -867,8 +861,8 @@
     property_get("persist.sys.sf.native_mode", value, "0");
     mDisplayColorSetting = static_cast<DisplayColorSetting>(atoi(value));
 
-    property_get("persist.sys.sf.color_mode", value, "0");
-    mForceColorMode = static_cast<ColorMode>(atoi(value));
+    mForceColorMode =
+            static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0));
 }
 
 void SurfaceFlinger::startBootAnim() {
@@ -934,16 +928,19 @@
 
     Mutex::Autolock lock(mStateLock);
 
-    const auto display = getDisplayDeviceLocked(displayToken);
-    if (!display) {
+    const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                                    .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                                    .and_then(getDisplayDeviceAndSnapshot());
+
+    if (!displayOpt) {
         return NAME_NOT_FOUND;
     }
 
-    if (const auto connectionType = display->getConnectionType())
-        info->connectionType = *connectionType;
-    else {
-        return INVALID_OPERATION;
-    }
+    const auto& [display, snapshotRef] = *displayOpt;
+    const auto& snapshot = snapshotRef.get();
+
+    info->connectionType = snapshot.connectionType();
+    info->deviceProductInfo = snapshot.deviceProductInfo();
 
     if (mEmulatedDisplayDensity) {
         info->density = mEmulatedDisplayDensity;
@@ -955,7 +952,6 @@
     info->density /= ACONFIGURATION_DENSITY_MEDIUM;
 
     info->secure = display->isSecure();
-    info->deviceProductInfo = display->getDeviceProductInfo();
     info->installOrientation = display->getPhysicalOrientation();
 
     return NO_ERROR;
@@ -969,23 +965,22 @@
 
     Mutex::Autolock lock(mStateLock);
 
-    const auto display = getDisplayDeviceLocked(displayToken);
-    if (!display) {
+    const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                                    .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                                    .and_then(getDisplayDeviceAndSnapshot());
+    if (!displayOpt) {
         return NAME_NOT_FOUND;
     }
 
-    const auto displayId = PhysicalDisplayId::tryCast(display->getId());
-    if (!displayId) {
-        return INVALID_OPERATION;
-    }
+    const auto& [display, snapshotRef] = *displayOpt;
+    const auto& snapshot = snapshotRef.get();
 
-    info->activeDisplayModeId = display->getActiveMode()->getId().value();
+    const auto& displayModes = snapshot.displayModes();
 
-    const auto& supportedModes = display->getSupportedModes();
     info->supportedDisplayModes.clear();
-    info->supportedDisplayModes.reserve(supportedModes.size());
+    info->supportedDisplayModes.reserve(displayModes.size());
 
-    for (const auto& [id, mode] : supportedModes) {
+    for (const auto& [id, mode] : displayModes) {
         ui::DisplayMode outMode;
         outMode.id = static_cast<int32_t>(id.value());
 
@@ -1029,21 +1024,25 @@
         info->supportedDisplayModes.push_back(outMode);
     }
 
+    info->supportedColorModes = snapshot.filterColorModes(mSupportsWideColor);
+
+    const PhysicalDisplayId displayId = snapshot.displayId();
+
+    info->activeDisplayModeId = display->refreshRateConfigs().getActiveModePtr()->getId().value();
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
-    info->supportedColorModes = getDisplayColorModes(*display);
     info->hdrCapabilities = display->getHdrCapabilities();
 
     info->autoLowLatencyModeSupported =
-            getHwComposer().hasDisplayCapability(*displayId,
+            getHwComposer().hasDisplayCapability(displayId,
                                                  DisplayCapability::AUTO_LOW_LATENCY_MODE);
     info->gameContentTypeSupported =
-            getHwComposer().supportsContentType(*displayId, hal::ContentType::GAME);
+            getHwComposer().supportsContentType(displayId, hal::ContentType::GAME);
 
     info->preferredBootDisplayMode = static_cast<ui::DisplayModeId>(-1);
 
     if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) {
-        if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(*displayId)) {
-            if (const auto modeId = display->translateModeId(*hwcId)) {
+        if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) {
+            if (const auto modeId = snapshot.translateModeId(*hwcId)) {
                 info->preferredBootDisplayMode = modeId->value();
             }
         }
@@ -1058,7 +1057,7 @@
     }
 
     const auto& schedule = mScheduler->getVsyncSchedule();
-    outStats->vsyncTime = schedule.vsyncDeadlineAfter(scheduler::SchedulerClock::now()).ns();
+    outStats->vsyncTime = schedule.vsyncDeadlineAfter(TimePoint::now()).ns();
     outStats->vsyncPeriod = schedule.period().ns();
     return NO_ERROR;
 }
@@ -1091,42 +1090,50 @@
     }
 }
 
-status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) {
+status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken,
+                                                   DisplayModeId modeId) {
     ATRACE_CALL();
 
     if (!displayToken) {
         return BAD_VALUE;
     }
 
-    auto future = mScheduler->schedule([=]() -> status_t {
-        const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
-        if (!display) {
-            ALOGE("Attempt to set allowed display modes for invalid display token %p",
-                  displayToken.get());
+    const char* const whence = __func__;
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
+        const auto displayOpt =
+                FTL_FAKE_GUARD(mStateLock,
+                               ftl::find_if(mPhysicalDisplays,
+                                            PhysicalDisplay::hasToken(displayToken))
+                                       .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                                       .and_then(getDisplayDeviceAndSnapshot()));
+        if (!displayOpt) {
+            ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
             return NAME_NOT_FOUND;
         }
 
-        if (display->isVirtual()) {
-            ALOGW("Attempt to set allowed display modes for virtual display");
-            return INVALID_OPERATION;
-        }
+        const auto& [display, snapshotRef] = *displayOpt;
+        const auto& snapshot = snapshotRef.get();
 
-        const auto mode = display->getMode(DisplayModeId{modeId});
-        if (!mode) {
-            ALOGW("Attempt to switch to an unsupported mode %d.", modeId);
+        const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
+                [](const DisplayModePtr& mode) { return mode->getFps(); });
+
+        if (!fpsOpt) {
+            ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
+                  to_string(snapshot.displayId()).c_str());
             return BAD_VALUE;
         }
 
-        const auto fps = mode->getFps();
-        // Keep the old switching type.
-        const auto allowGroupSwitching =
-                display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching;
-        const scheduler::RefreshRateConfigs::Policy policy{mode->getId(),
-                                                           allowGroupSwitching,
-                                                           {fps, fps}};
-        constexpr bool kOverridePolicy = false;
+        const Fps fps = *fpsOpt;
 
-        return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
+        // Keep the old switching type.
+        const bool allowGroupSwitching =
+                display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching;
+
+        const scheduler::RefreshRateConfigs::DisplayManagerPolicy policy{modeId,
+                                                                         allowGroupSwitching,
+                                                                         {fps, fps}};
+
+        return setDesiredDisplayModeSpecsInternal(display, policy);
     });
 
     return future.get();
@@ -1140,16 +1147,14 @@
         return;
     }
 
-    const auto upcomingModeInfo =
-            FTL_FAKE_GUARD(kMainThreadContext, display->getUpcomingActiveMode());
-
+    const auto upcomingModeInfo = display->getUpcomingActiveMode();
     if (!upcomingModeInfo.mode) {
         // There is no pending mode change. This can happen if the active
         // display changed and the mode change happened on a different display.
         return;
     }
 
-    if (display->getActiveMode()->getResolution() != upcomingModeInfo.mode->getResolution()) {
+    if (display->getActiveMode().getResolution() != upcomingModeInfo.mode->getResolution()) {
         auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
         // We need to generate new sequenceId in order to recreate the display (and this
         // way the framebuffer).
@@ -1161,9 +1166,12 @@
         return;
     }
 
-    // We just created this display so we can call even if we are not on the main thread.
-    ftl::FakeGuard guard(kMainThreadContext);
-    display->setActiveMode(upcomingModeInfo.mode->getId());
+    mPhysicalDisplays.get(display->getPhysicalId())
+            .transform(&PhysicalDisplay::snapshotRef)
+            .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+                FTL_FAKE_GUARD(kMainThreadContext,
+                               display->setActiveMode(upcomingModeInfo.mode->getId(), snapshot));
+            }));
 
     const Fps refreshRate = upcomingModeInfo.mode->getFps();
     mRefreshRateStats->setRefreshRate(refreshRate);
@@ -1176,7 +1184,7 @@
 
 void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) {
     display->clearDesiredActiveModeState();
-    if (isDisplayActiveLocked(display)) {
+    if (display->getPhysicalId() == mActiveDisplayId) {
         mScheduler->setModeChangePending(false);
     }
 }
@@ -1193,39 +1201,46 @@
 
     std::optional<PhysicalDisplayId> displayToUpdateImmediately;
 
-    for (const auto& iter : mDisplays) {
-        const auto& display = iter.second;
-        if (!display || !display->isInternal()) {
+    for (const auto& [id, physical] : mPhysicalDisplays) {
+        const auto& snapshot = physical.snapshot();
+
+        if (snapshot.connectionType() != ui::DisplayConnectionType::Internal) {
             continue;
         }
 
+        const auto display = getDisplayDeviceLocked(id);
+        if (!display) continue;
+
         // Store the local variable to release the lock.
         const auto desiredActiveMode = display->getDesiredActiveMode();
         if (!desiredActiveMode) {
-            // No desired active mode pending to be applied
+            // No desired active mode pending to be applied.
             continue;
         }
 
-        if (!isDisplayActiveLocked(display)) {
-            // display is no longer the active display, so abort the mode change
+        if (id != mActiveDisplayId) {
+            // Display is no longer the active display, so abort the mode change.
             clearDesiredActiveModeState(display);
             continue;
         }
 
-        const auto desiredMode = display->getMode(desiredActiveMode->mode->getId());
-        if (!desiredMode) {
+        const auto desiredModeId = desiredActiveMode->mode->getId();
+        const auto refreshRateOpt =
+                snapshot.displayModes()
+                        .get(desiredModeId)
+                        .transform([](const DisplayModePtr& mode) { return mode->getFps(); });
+
+        if (!refreshRateOpt) {
             ALOGW("Desired display mode is no longer supported. Mode ID = %d",
-                  desiredActiveMode->mode->getId().value());
+                  desiredModeId.value());
             clearDesiredActiveModeState(display);
             continue;
         }
 
-        const auto refreshRate = desiredMode->getFps();
-        ALOGV("%s changing active mode to %d(%s) for display %s", __func__,
-              desiredMode->getId().value(), to_string(refreshRate).c_str(),
-              to_string(display->getId()).c_str());
+        ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
+              to_string(*refreshRateOpt).c_str(), to_string(display->getId()).c_str());
 
-        if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+        if (display->getActiveMode().getId() == desiredModeId) {
             // we are already in the requested mode, there is nothing left to do
             desiredActiveModeChangeDone(display);
             continue;
@@ -1234,8 +1249,7 @@
         // Desired active mode was set, it is different than the mode currently in use, however
         // allowed modes might have changed by the time we process the refresh.
         // Make sure the desired mode is still allowed
-        const auto displayModeAllowed =
-                display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId());
+        const auto displayModeAllowed = display->refreshRateConfigs().isModeAllowed(desiredModeId);
         if (!displayModeAllowed) {
             clearDesiredActiveModeState(display);
             continue;
@@ -1247,9 +1261,8 @@
         constraints.seamlessRequired = false;
         hal::VsyncPeriodChangeTimeline outTimeline;
 
-        const auto status = FTL_FAKE_GUARD(kMainThreadContext,
-                                           display->initiateModeChange(*desiredActiveMode,
-                                                                       constraints, &outTimeline));
+        const auto status =
+                display->initiateModeChange(*desiredActiveMode, constraints, &outTimeline);
 
         if (status != NO_ERROR) {
             // initiateModeChange may fail if a hotplug event is just about
@@ -1257,6 +1270,8 @@
             ALOGW("initiateModeChange failed: %d", status);
             continue;
         }
+
+        display->refreshRateConfigs().onModeChangeInitiated();
         mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
 
         if (outTimeline.refreshRequired) {
@@ -1276,7 +1291,7 @@
         const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
         const auto desiredActiveMode = display->getDesiredActiveMode();
         if (desiredActiveMode &&
-            display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+            display->getActiveMode().getId() == desiredActiveMode->mode->getId()) {
             desiredActiveModeChangeDone(display);
         }
     }
@@ -1297,22 +1312,6 @@
     future.wait();
 }
 
-std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) {
-    auto modes = getHwComposer().getColorModes(display.getPhysicalId());
-
-    // 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 (display.getConnectionType() == ui::DisplayConnectionType::Internal &&
-        !hasWideColorDisplay) {
-        const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
-        modes.erase(newEnd, modes.end());
-    }
-
-    return modes;
-}
-
 status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
                                                    ui::DisplayPrimaries& primaries) {
     if (!displayToken) {
@@ -1321,13 +1320,13 @@
 
     Mutex::Autolock lock(mStateLock);
 
-    const auto display = getDisplayDeviceLocked(displayToken);
+    const auto display = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                                 .transform(&ftl::to_mapped_ref<PhysicalDisplays>);
     if (!display) {
         return NAME_NOT_FOUND;
     }
 
-    const auto connectionType = display->getConnectionType();
-    if (connectionType != ui::DisplayConnectionType::Internal) {
+    if (!display.transform(&PhysicalDisplay::isInternal).value()) {
         return INVALID_OPERATION;
     }
 
@@ -1336,31 +1335,32 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
+status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode mode) {
     if (!displayToken) {
         return BAD_VALUE;
     }
 
+    const char* const whence = __func__;
     auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> 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());
+        const auto displayOpt =
+                ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                        .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                        .and_then(getDisplayDeviceAndSnapshot());
+
+        if (!displayOpt) {
+            ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
             return NAME_NOT_FOUND;
         }
 
-        if (display->isVirtual()) {
-            ALOGW("Attempt to set active color mode %s (%d) for virtual display",
-                  decodeColorMode(mode).c_str(), mode);
-            return INVALID_OPERATION;
-        }
+        const auto& [display, snapshotRef] = *displayOpt;
+        const auto& snapshot = snapshotRef.get();
 
-        const auto modes = getDisplayColorModes(*display);
+        const auto modes = snapshot.filterColorModes(mSupportsWideColor);
         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());
+        if (mode < ui::ColorMode::NATIVE || !exists) {
+            ALOGE("%s: Invalid color mode %s (%d) for display %s", whence,
+                  decodeColorMode(mode).c_str(), mode, to_string(snapshot.displayId()).c_str());
             return BAD_VALUE;
         }
 
@@ -1383,30 +1383,31 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken,
-                                            ui::DisplayModeId modeId) {
+status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken,
+                                            DisplayModeId modeId) {
     const char* const whence = __func__;
     auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
-        const auto display = getDisplayDeviceLocked(displayToken);
-        if (!display) {
-            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+        const auto snapshotOpt =
+                ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                        .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                        .transform(&PhysicalDisplay::snapshotRef);
+
+        if (!snapshotOpt) {
+            ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
             return NAME_NOT_FOUND;
         }
 
-        if (display->isVirtual()) {
-            ALOGE("%s: Invalid operation on virtual display", whence);
-            return INVALID_OPERATION;
-        }
+        const auto& snapshot = snapshotOpt->get();
+        const auto hwcIdOpt = snapshot.displayModes().get(modeId).transform(
+                [](const DisplayModePtr& mode) { return mode->getHwcId(); });
 
-        const auto displayId = display->getPhysicalId();
-        const auto mode = display->getMode(DisplayModeId{modeId});
-        if (!mode) {
-            ALOGE("%s: Invalid mode %d for display %s", whence, modeId,
-                  to_string(displayId).c_str());
+        if (!hwcIdOpt) {
+            ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
+                  to_string(snapshot.displayId()).c_str());
             return BAD_VALUE;
         }
 
-        return getHwComposer().setBootDisplayMode(displayId, mode->getHwcId());
+        return getHwComposer().setBootDisplayMode(snapshot.displayId(), *hwcIdOpt);
     });
     return future.get();
 }
@@ -1537,31 +1538,10 @@
     }
 
     *outIsWideColorDisplay =
-            display->isPrimary() ? hasWideColorDisplay : display->hasWideColorGamut();
+            display->isPrimary() ? mSupportsWideColor : display->hasWideColorGamut();
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
-    auto future = mScheduler->schedule([=] {
-        Mutex::Autolock lock(mStateLock);
-
-        if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
-            mScheduler->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
-        }
-    });
-
-    future.wait();
-    return NO_ERROR;
-}
-
-status_t SurfaceFlinger::injectVSync(nsecs_t when) {
-    Mutex::Autolock lock(mStateLock);
-    const nsecs_t expectedPresentTime = calculateExpectedPresentTime(TimePoint::fromNs(when)).ns();
-    const nsecs_t deadlineTimestamp = expectedPresentTime;
-    return mScheduler->injectVSync(when, expectedPresentTime, deadlineTimestamp) ? NO_ERROR
-                                                                                 : BAD_VALUE;
-}
-
 status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
     outLayers->clear();
     auto future = mScheduler->schedule([=] {
@@ -1846,20 +1826,13 @@
     ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
 
     Mutex::Autolock lock(mStateLock);
-    const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
-    if (displayId) {
-        const auto token = getPhysicalDisplayTokenLocked(*displayId);
-        const auto display = getDisplayDeviceLocked(token);
-        display->onVsync(timestamp);
-    }
 
     if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
         return;
     }
 
-    const bool isActiveDisplay =
-            displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken;
-    if (!isActiveDisplay) {
+    if (const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
+        displayId != mActiveDisplayId) {
         // For now, we don't do anything with non active display vsyncs.
         return;
     }
@@ -1873,23 +1846,14 @@
 
 void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
                                           hal::Connection connection) {
-    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
-    // the main thread with the state lock already held, so don't attempt to
-    // acquire it here.
-    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
-    mPendingHotplugEvents.emplace_back(HotplugEvent{hwcDisplayId, connection});
-
-    if (std::this_thread::get_id() == mMainThreadId) {
-        // Process all pending hot plug events immediately if we are on the main thread.
-        processDisplayHotplugEventsLocked();
+    {
+        std::lock_guard<std::mutex> lock(mHotplugMutex);
+        mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
     }
 
-    setTransactionFlags(eDisplayTransactionNeeded);
+    if (mScheduler) {
+        mScheduler->scheduleConfigure();
+    }
 }
 
 void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged(
@@ -1962,6 +1926,13 @@
     return vsyncDeadline + schedule.period();
 }
 
+void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
+    Mutex::Autolock lock(mStateLock);
+    if (configureLocked()) {
+        setTransactionFlags(eDisplayTransactionNeeded);
+    }
+}
+
 bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
         FTL_FAKE_GUARD(kMainThreadContext) {
     // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
@@ -1976,7 +1947,7 @@
                                                           : calculateExpectedPresentTime(frameTime);
 
     ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId.value,
-                  ticks<std::milli, float>(mExpectedPresentTime - scheduler::SchedulerClock::now()),
+                  ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
                   mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
 
     const Period vsyncPeriod = mScheduler->getVsyncSchedule().period();
@@ -2057,22 +2028,21 @@
 
     // Save this once per commit + composite to ensure consistency
     // TODO (b/240619471): consider removing active display check once AOD is fixed
-    const auto activeDisplay =
-            FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken));
+    const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId));
     mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
             activeDisplay->getPowerMode() == hal::PowerMode::ON;
     if (mPowerHintSessionEnabled) {
         const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
-        const nsecs_t vsyncPeriod = display->getActiveMode()->getVsyncPeriod();
-        mPowerAdvisor->setCommitStart(frameTime.ns());
-        mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime.ns());
+        const Period vsyncPeriod = Period::fromNs(display->getActiveMode().getVsyncPeriod());
+        mPowerAdvisor->setCommitStart(frameTime);
+        mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
 
         // Frame delay is how long we should have minus how long we actually have.
         const Duration idealSfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration;
         const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime);
 
-        mPowerAdvisor->setFrameDelay(frameDelay.ns());
-        mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration.ns());
+        mPowerAdvisor->setFrameDelay(frameDelay);
+        mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
         mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
 
         // Send early hint here to make sure there's not another frame pending
@@ -2133,7 +2103,7 @@
     // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
     // and may eventually call to ~Layer() if it holds the last reference
     {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock lock(mStateLock);
         mScheduler->chooseRefreshRateForContent();
         setActiveModeInHwcIfNeeded();
     }
@@ -2165,10 +2135,13 @@
         displayIds.push_back(display->getId());
     }
     mPowerAdvisor->setDisplays(displayIds);
-    mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
-        if (auto layerFE = layer->getCompositionEngineLayerFE())
-            refreshArgs.layers.push_back(layerFE);
-    });
+
+    const bool updateTaskMetadata = mCompositionEngine->getFeatureFlags().test(
+            compositionengine::Feature::kSnapshotLayerMetadata);
+    if (updateTaskMetadata && (mVisibleRegionsDirty || mLayerMetadataSnapshotNeeded)) {
+        updateLayerMetadataSnapshot();
+        mLayerMetadataSnapshotNeeded = false;
+    }
 
     if (DOES_CONTAIN_BORDER) {
         refreshArgs.borderInfoList.clear();
@@ -2199,6 +2172,12 @@
 
     refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
     refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
+    mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
+        layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
+        if (auto layerFE = layer->getCompositionEngineLayerFE()) {
+            refreshArgs.layers.push_back(layerFE);
+        }
+    });
     refreshArgs.blursAreExpensive = mBlursAreExpensive;
     refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
 
@@ -2232,8 +2211,9 @@
 
     // Send a power hint hint after presentation is finished
     if (mPowerHintSessionEnabled) {
-        mPowerAdvisor->setSfPresentTiming(mPreviousPresentFences[0].fenceTime->getSignalTime(),
-                                          systemTime());
+        mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(mPreviousPresentFences[0]
+                                                                    .fenceTime->getSignalTime()),
+                                          TimePoint::now());
         if (mPowerHintSessionMode.late) {
             mPowerAdvisor->sendActualWorkDuration();
         }
@@ -2283,7 +2263,7 @@
     }
 
     if (mPowerHintSessionEnabled) {
-        mPowerAdvisor->setCompositeEnd(systemTime());
+        mPowerAdvisor->setCompositeEnd(TimePoint::now());
     }
 }
 
@@ -2380,7 +2360,7 @@
     auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
     mPreviousPresentFences[0] = {presentFence, presentFenceTime};
 
-    const TimePoint presentTime = scheduler::SchedulerClock::now();
+    const TimePoint presentTime = TimePoint::now();
 
     // Set presentation information before calling Layer::releasePendingBuffer, such that jank
     // information from previous' frame classification is already available when sending jank info
@@ -2470,7 +2450,13 @@
     mTimeStats->incrementTotalFrames();
     mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
-    if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON &&
+    const bool isInternalDisplay = display &&
+            FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)
+                    .get(display->getPhysicalId())
+                    .transform(&PhysicalDisplay::isInternal)
+                    .value_or(false);
+
+    if (isInternalDisplay && display && display->getPowerMode() == hal::PowerMode::ON &&
         presentFenceTime->isValid()) {
         mScheduler->addPresentFence(std::move(presentFenceTime));
     }
@@ -2493,20 +2479,6 @@
         return;
     }
 
-    if (mHasPoweredOff) {
-        mHasPoweredOff = false;
-    } else {
-        const Duration elapsedTime = presentTime - getBE().mLastPresentTime;
-        const size_t numPeriods = static_cast<size_t>(elapsedTime.ns() / vsyncPeriod.ns());
-        if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
-            getBE().mFrameBuckets[numPeriods] += elapsedTime.ns();
-        } else {
-            getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] += elapsedTime.ns();
-        }
-        getBE().mTotalTime += elapsedTime.ns();
-    }
-    getBE().mLastPresentTime = presentTime;
-
     // Cleanup any outstanding resources due to rendering a prior frame.
     getRenderEngine().cleanupPostRender();
 
@@ -2599,10 +2571,9 @@
     do {
         hwcModes = getHwComposer().getModes(displayId);
         activeModeHwcId = getHwComposer().getActiveMode(displayId);
-        LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
 
         const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
-            return mode.hwcId == *activeModeHwcId;
+            return mode.hwcId == activeModeHwcId;
         };
 
         if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) {
@@ -2610,16 +2581,21 @@
         }
     } while (++attempt < kMaxAttempts);
 
-    LOG_ALWAYS_FATAL_IF(attempt == kMaxAttempts,
-                        "After %d attempts HWC still returns an active mode which is not"
-                        " supported. Active mode ID = %" PRIu64 ". Supported modes = %s",
-                        kMaxAttempts, *activeModeHwcId, base::Join(hwcModes, ", ").c_str());
-
-    DisplayModes oldModes;
-    if (const auto token = getPhysicalDisplayTokenLocked(displayId)) {
-        oldModes = getDisplayDeviceLocked(token)->getSupportedModes();
+    if (attempt == kMaxAttempts) {
+        const std::string activeMode =
+                activeModeHwcId ? std::to_string(*activeModeHwcId) : "unknown"s;
+        ALOGE("HWC failed to report an active mode that is supported: activeModeHwcId=%s, "
+              "hwcModes={%s}",
+              activeMode.c_str(), base::Join(hwcModes, ", ").c_str());
+        return {};
     }
 
+    const DisplayModes oldModes = mPhysicalDisplays.get(displayId)
+                                          .transform([](const PhysicalDisplay& display) {
+                                              return display.snapshot().displayModes();
+                                          })
+                                          .value_or(DisplayModes{});
+
     ui::DisplayModeId nextModeId = 1 +
             std::accumulate(oldModes.begin(), oldModes.end(), static_cast<ui::DisplayModeId>(-1),
                             [](ui::DisplayModeId max, const auto& pair) {
@@ -2657,70 +2633,96 @@
     return {modes, activeMode};
 }
 
-void SurfaceFlinger::processDisplayHotplugEventsLocked() {
-    for (const auto& event : mPendingHotplugEvents) {
-        std::optional<DisplayIdentificationInfo> info =
-                getHwComposer().onHotplug(event.hwcDisplayId, event.connection);
-
-        if (!info) {
-            continue;
-        }
-
-        const auto displayId = info->id;
-        const auto token = mPhysicalDisplayTokens.get(displayId);
-
-        if (event.connection == hal::Connection::CONNECTED) {
-            auto [supportedModes, activeMode] = loadDisplayModes(displayId);
-
-            if (!token) {
-                ALOGV("Creating display %s", to_string(displayId).c_str());
-
-                DisplayDeviceState state;
-                state.physical = {.id = displayId,
-                                  .type = getHwComposer().getDisplayConnectionType(displayId),
-                                  .hwcDisplayId = event.hwcDisplayId,
-                                  .deviceProductInfo = std::move(info->deviceProductInfo),
-                                  .supportedModes = std::move(supportedModes),
-                                  .activeMode = std::move(activeMode)};
-                state.isSecure = true; // All physical displays are currently considered secure.
-                state.displayName = std::move(info->name);
-
-                sp<IBinder> token = sp<BBinder>::make();
-                mCurrentState.displays.add(token, state);
-                mPhysicalDisplayTokens.try_emplace(displayId, std::move(token));
-                mInterceptor->saveDisplayCreation(state);
-            } else {
-                ALOGV("Recreating display %s", to_string(displayId).c_str());
-
-                auto& state = mCurrentState.displays.editValueFor(token->get());
-                state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
-                state.physical->supportedModes = std::move(supportedModes);
-                state.physical->activeMode = std::move(activeMode);
-                if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
-                    state.physical->deviceProductInfo = std::move(info->deviceProductInfo);
-                }
-            }
-        } else {
-            ALOGV("Removing display %s", to_string(displayId).c_str());
-
-            if (const ssize_t index = mCurrentState.displays.indexOfKey(token->get()); index >= 0) {
-                const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
-                mInterceptor->saveDisplayDeletion(state.sequenceId);
-                mCurrentState.displays.removeItemsAt(index);
-            }
-
-            mPhysicalDisplayTokens.erase(displayId);
-        }
-
-        processDisplayChangesLocked();
+bool SurfaceFlinger::configureLocked() {
+    std::vector<HotplugEvent> events;
+    {
+        std::lock_guard<std::mutex> lock(mHotplugMutex);
+        events = std::move(mPendingHotplugEvents);
     }
 
-    mPendingHotplugEvents.clear();
+    for (const auto [hwcDisplayId, connection] : events) {
+        if (auto info = getHwComposer().onHotplug(hwcDisplayId, connection)) {
+            const auto displayId = info->id;
+            const bool connected = connection == hal::Connection::CONNECTED;
+
+            if (const char* const log =
+                        processHotplug(displayId, hwcDisplayId, connected, std::move(*info))) {
+                ALOGI("%s display %s (HAL ID %" PRIu64 ")", log, to_string(displayId).c_str(),
+                      hwcDisplayId);
+            }
+        }
+    }
+
+    return !events.empty();
+}
+
+const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId,
+                                           hal::HWDisplayId hwcDisplayId, bool connected,
+                                           DisplayIdentificationInfo&& info) {
+    const auto displayOpt = mPhysicalDisplays.get(displayId);
+    if (!connected) {
+        LOG_ALWAYS_FATAL_IF(!displayOpt);
+        const auto& display = displayOpt->get();
+
+        if (const ssize_t index = mCurrentState.displays.indexOfKey(display.token()); index >= 0) {
+            mCurrentState.displays.removeItemsAt(index);
+        }
+
+        mPhysicalDisplays.erase(displayId);
+        return "Disconnecting";
+    }
+
+    auto [displayModes, activeMode] = loadDisplayModes(displayId);
+    if (!activeMode) {
+        // TODO(b/241286153): Report hotplug failure to the framework.
+        ALOGE("Failed to hotplug display %s", to_string(displayId).c_str());
+        getHwComposer().disconnectDisplay(displayId);
+        return nullptr;
+    }
+
+    ui::ColorModes colorModes = getHwComposer().getColorModes(displayId);
+
+    if (displayOpt) {
+        const auto& display = displayOpt->get();
+        const auto& snapshot = display.snapshot();
+
+        std::optional<DeviceProductInfo> deviceProductInfo;
+        if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
+            deviceProductInfo = std::move(info.deviceProductInfo);
+        } else {
+            deviceProductInfo = snapshot.deviceProductInfo();
+        }
+
+        const auto it =
+                mPhysicalDisplays.try_replace(displayId, display.token(), displayId,
+                                              snapshot.connectionType(), std::move(displayModes),
+                                              std::move(colorModes), std::move(deviceProductInfo));
+
+        auto& state = mCurrentState.displays.editValueFor(it->second.token());
+        state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
+        state.physical->activeMode = std::move(activeMode);
+        return "Reconnecting";
+    }
+
+    const sp<IBinder> token = sp<BBinder>::make();
+
+    mPhysicalDisplays.try_emplace(displayId, token, displayId,
+                                  getHwComposer().getDisplayConnectionType(displayId),
+                                  std::move(displayModes), std::move(colorModes),
+                                  std::move(info.deviceProductInfo));
+
+    DisplayDeviceState state;
+    state.physical = {.id = displayId,
+                      .hwcDisplayId = hwcDisplayId,
+                      .activeMode = std::move(activeMode)};
+    state.isSecure = true; // All physical displays are currently considered secure.
+    state.displayName = std::move(info.name);
+
+    mCurrentState.displays.add(token, state);
+    return "Connecting";
 }
 
 void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    ALOGI("Dispatching display hotplug event displayId=%s, connected=%d",
-          to_string(displayId).c_str(), connected);
     mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected);
     mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
 }
@@ -2740,8 +2742,6 @@
     creationArgs.supportedPerFrameMetadata = 0;
 
     if (const auto& physical = state.physical) {
-        creationArgs.connectionType = physical->type;
-        creationArgs.supportedModes = physical->supportedModes;
         creationArgs.activeModeId = physical->activeMode->getId();
         const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
                 getKernelIdleTimerProperties(compositionDisplay->getId());
@@ -2752,25 +2752,31 @@
                          base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
                  .idleTimerTimeout = idleTimerTimeoutMs,
                  .kernelIdleTimerController = kernelIdleTimerController};
-        creationArgs.refreshRateConfigs =
-                std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes,
-                                                                creationArgs.activeModeId, config);
-    }
 
-    if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
-        creationArgs.isPrimary = id == getPrimaryDisplayIdLocked();
+        creationArgs.refreshRateConfigs =
+                mPhysicalDisplays.get(physical->id)
+                        .transform(&PhysicalDisplay::snapshotRef)
+                        .transform([&](const display::DisplaySnapshot& snapshot) {
+                            return std::make_shared<
+                                    scheduler::RefreshRateConfigs>(snapshot.displayModes(),
+                                                                   creationArgs.activeModeId,
+                                                                   config);
+                        })
+                        .value_or(nullptr);
+
+        creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked();
 
         if (useColorManagement) {
-            std::vector<ColorMode> modes = getHwComposer().getColorModes(*id);
-            for (ColorMode colorMode : modes) {
-                if (isWideColorMode(colorMode)) {
-                    creationArgs.hasWideColorGamut = true;
-                }
-
-                std::vector<RenderIntent> renderIntents =
-                        getHwComposer().getRenderIntents(*id, colorMode);
-                creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
-            }
+            mPhysicalDisplays.get(physical->id)
+                    .transform(&PhysicalDisplay::snapshotRef)
+                    .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+                        for (const auto mode : snapshot.colorModes()) {
+                            creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
+                            creationArgs.hwcColorModes
+                                    .emplace(mode,
+                                             getHwComposer().getRenderIntents(physical->id, mode));
+                        }
+                    }));
         }
     }
 
@@ -2795,29 +2801,34 @@
     ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
 
     // virtual displays are always considered enabled
-    creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+    creationArgs.initialPowerMode =
+            state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt;
 
     sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
 
     nativeWindowSurface->preallocateBuffers();
 
-    ColorMode defaultColorMode = ColorMode::NATIVE;
+    ui::ColorMode defaultColorMode = ui::ColorMode::NATIVE;
     Dataspace defaultDataSpace = Dataspace::UNKNOWN;
     if (display->hasWideColorGamut()) {
-        defaultColorMode = ColorMode::SRGB;
+        defaultColorMode = ui::ColorMode::SRGB;
         defaultDataSpace = Dataspace::V0_SRGB;
     }
     display->getCompositionDisplay()->setColorProfile(
             compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace,
                                                     RenderIntent::COLORIMETRIC,
                                                     Dataspace::UNKNOWN});
-    if (!state.isVirtual()) {
-        FTL_FAKE_GUARD(kMainThreadContext,
-                       display->setActiveMode(state.physical->activeMode->getId()));
-        display->setDeviceProductInfo(state.physical->deviceProductInfo);
+
+    if (const auto& physical = state.physical) {
+        mPhysicalDisplays.get(physical->id)
+                .transform(&PhysicalDisplay::snapshotRef)
+                .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+                    FTL_FAKE_GUARD(kMainThreadContext,
+                                   display->setActiveMode(physical->activeMode->getId(), snapshot));
+                }));
     }
 
-    display->setLayerStack(state.layerStack);
+    display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
     display->setProjection(state.orientation, state.layerStackSpaceRect,
                            state.orientedDisplaySpaceRect);
     display->setDisplayName(state.displayName);
@@ -2893,10 +2904,13 @@
     LOG_FATAL_IF(!displaySurface);
     auto display = setupNewDisplayDeviceInternal(displayToken, std::move(compositionDisplay), state,
                                                  displaySurface, producer);
-    if (display->isPrimary()) {
-        initScheduler(display);
-    }
-    if (!state.isVirtual()) {
+
+    if (mScheduler && !display->isVirtual()) {
+        // Display modes are reloaded on hotplug reconnect.
+        if (display->isPrimary()) {
+            mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
+        }
+        mScheduler->registerDisplay(display);
         dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
     }
 
@@ -2912,6 +2926,7 @@
             releaseVirtualDisplay(display->getVirtualId());
         } else {
             dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
+            mScheduler->unregisterDisplay(display->getPhysicalId());
         }
     }
 
@@ -2948,6 +2963,8 @@
             display->disconnect();
             if (display->isVirtual()) {
                 releaseVirtualDisplay(display->getVirtualId());
+            } else {
+                mScheduler->unregisterDisplay(display->getPhysicalId());
             }
         }
 
@@ -2973,7 +2990,8 @@
 
     if (const auto display = getDisplayDeviceLocked(displayToken)) {
         if (currentState.layerStack != drawingState.layerStack) {
-            display->setLayerStack(currentState.layerStack);
+            display->setLayerFilter(
+                    makeLayerFilterForDisplay(display->getId(), currentState.layerStack));
         }
         if (currentState.flags != drawingState.flags) {
             display->setFlags(currentState.flags);
@@ -2983,7 +3001,7 @@
             (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
             display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
                                    currentState.orientedDisplaySpaceRect);
-            if (isDisplayActiveLocked(display)) {
+            if (display->getId() == mActiveDisplayId) {
                 mActiveDisplayTransformHint = display->getTransformHint();
             }
         }
@@ -2991,15 +3009,16 @@
             currentState.height != drawingState.height) {
             display->setDisplaySize(currentState.width, currentState.height);
 
-            if (isDisplayActiveLocked(display)) {
+            if (display->getId() == mActiveDisplayId) {
                 onActiveDisplaySizeChanged(display);
             }
         }
     }
 }
+
 void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
     mVsyncConfiguration->reset();
-    const Fps refreshRate = activeDisplay->refreshRateConfigs().getActiveMode()->getFps();
+    const Fps refreshRate = activeDisplay->getActiveMode().getFps();
     updatePhaseConfiguration(refreshRate);
     mRefreshRateStats->setRefreshRate(refreshRate);
 }
@@ -3050,7 +3069,6 @@
     const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
     if (displayTransactionNeeded) {
         processDisplayChangesLocked();
-        processDisplayHotplugEventsLocked();
     }
     mForceTransactionDisplayChange = displayTransactionNeeded;
 
@@ -3141,7 +3159,6 @@
     }
 
     doCommitTransactions();
-    signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
 }
 
 void SurfaceFlinger::updateInputFlinger() {
@@ -3220,7 +3237,7 @@
 
 void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
                                       std::vector<DisplayInfo>& outDisplayInfos) {
-    ftl::SmallMap<ui::LayerStack, DisplayDevice::InputInfo, 4> displayInputInfos;
+    display::DisplayMap<ui::LayerStack, DisplayDevice::InputInfo> displayInputInfos;
 
     for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
         const auto layerStack = display->getLayerStack();
@@ -3250,10 +3267,11 @@
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (!layer->needsInputInfo()) return;
 
-        const auto opt = displayInputInfos.get(layer->getLayerStack(),
-                                               [](const auto& info) -> Layer::InputDisplayArgs {
-                                                   return {&info.transform, info.isSecure};
-                                               });
+        const auto opt = displayInputInfos.get(layer->getLayerStack())
+                                 .transform([](const DisplayDevice::InputInfo& info) {
+                                     return Layer::InputDisplayArgs{&info.transform, info.isSecure};
+                                 });
+
         outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
     });
 
@@ -3276,25 +3294,34 @@
     mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
-void SurfaceFlinger::requestDisplayMode(DisplayModePtr mode, DisplayModeEvent event) {
+void SurfaceFlinger::requestDisplayModes(
+        std::vector<scheduler::DisplayModeConfig> displayModeConfigs) {
+    if (mBootStage != BootStage::FINISHED) {
+        ALOGV("Currently in the boot stage, skipping display mode changes");
+        return;
+    }
+
+    ATRACE_CALL();
     // 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);
 
-    const auto display = getDefaultDisplayDeviceLocked();
-    if (!display || mBootStage != BootStage::FINISHED) {
-        return;
-    }
-    ATRACE_CALL();
-
-    if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) {
-        ALOGV("Skipping disallowed mode %d", mode->getId().value());
-        return;
-    }
-
-    setDesiredActiveMode({std::move(mode), event});
+    std::for_each(displayModeConfigs.begin(), displayModeConfigs.end(),
+                  [&](const auto& config) REQUIRES(mStateLock) {
+                      const auto& displayModePtr = config.displayModePtr;
+                      if (const auto display =
+                                  getDisplayDeviceLocked(displayModePtr->getPhysicalDisplayId());
+                          display->refreshRateConfigs().isModeAllowed(displayModePtr->getId())) {
+                          const auto event = config.signals.idle ? DisplayModeEvent::None
+                                                                 : DisplayModeEvent::Changed;
+                          setDesiredActiveMode({displayModePtr, event});
+                      } else {
+                          ALOGV("Skipping disallowed mode %d for display %" PRId64,
+                                displayModePtr->getId().value(), display->getPhysicalId().value);
+                      }
+                  });
 }
 
 void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3306,20 +3333,16 @@
     mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
 }
 
-void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {
-    if (mScheduler) {
-        // If the scheduler is already initialized, this means that we received
-        // a hotplug(connected) on the primary display. In that case we should
-        // update the scheduler with the most recent display information.
-        ALOGW("Scheduler already initialized, updating instead");
-        mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
-        return;
-    }
-    const auto currRefreshRate = display->getActiveMode()->getFps();
-    mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
-                                                                      hal::PowerMode::OFF);
+void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
+    LOG_ALWAYS_FATAL_IF(mScheduler);
 
-    mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
+    const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr();
+    const Fps activeRefreshRate = activeModePtr->getFps();
+    mRefreshRateStats =
+            std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate,
+                                                          hal::PowerMode::OFF);
+
+    mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate);
     mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
 
     using Feature = scheduler::Feature;
@@ -3347,24 +3370,21 @@
 
         mScheduler->createVsyncSchedule(features);
         mScheduler->setRefreshRateConfigs(std::move(configs));
+        mScheduler->registerDisplay(display);
     }
     setVsyncEnabled(false);
     mScheduler->startTimers();
 
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
-    const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
+    const nsecs_t vsyncPeriod = activeRefreshRate.getPeriodNsecs();
     mAppConnectionHandle =
             mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
                                          /*workDuration=*/configs.late.appWorkDuration,
-                                         /*readyDuration=*/configs.late.sfWorkDuration,
-                                         impl::EventThread::InterceptVSyncsCallback());
+                                         /*readyDuration=*/configs.late.sfWorkDuration);
     mSfConnectionHandle =
             mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
                                          /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
-                                         /*readyDuration=*/configs.late.sfWorkDuration,
-                                         [this](nsecs_t timestamp) {
-                                             mInterceptor->saveVSyncEvent(timestamp);
-                                         });
+                                         /*readyDuration=*/configs.late.sfWorkDuration);
 
     mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
                           *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
@@ -3380,7 +3400,7 @@
     // This is a bit hacky, but this avoids a back-pointer into the main SF
     // 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());
+    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
 }
 
 void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
@@ -3572,15 +3592,16 @@
         return NO_MEMORY;
     }
 
+    layer->updateTransformHint(mActiveDisplayTransformHint);
+    if (outTransformHint) {
+        *outTransformHint = mActiveDisplayTransformHint;
+    }
+
     {
         std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
         mCreatedLayers.emplace_back(layer, parent, addToRoot);
     }
 
-    layer->updateTransformHint(mActiveDisplayTransformHint);
-    if (outTransformHint) {
-        *outTransformHint = mActiveDisplayTransformHint;
-    }
     // attach this layer to the client
     if (client != nullptr) {
         client->attachLayer(handle, layer);
@@ -3607,122 +3628,117 @@
     }
 }
 
-int SurfaceFlinger::flushPendingTransactionQueues(
-        std::vector<TransactionState>& transactions,
-        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-        bool tryApplyUnsignaled) {
-    std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
-    int transactionsPendingBarrier = 0;
-    auto it = mPendingTransactionQueues.begin();
-    while (it != mPendingTransactionQueues.end()) {
-        auto& [applyToken, transactionQueue] = *it;
-        while (!transactionQueue.empty()) {
-            // if we are in LatchUnsignaledConfig::AutoSingleLayer
-            // then we should have only one applyToken for processing.
-            // so we can stop further transactions on this applyToken.
-            if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer &&
-                !applyTokensWithUnsignaledTransactions.empty()) {
-                ATRACE_NAME("stopTransactionProcessing");
-                break;
-            }
-
-            auto& transaction = transactionQueue.front();
-            const auto ready =
-                    transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo,
-                                                  transaction.isAutoTimestamp,
-                                                  TimePoint::fromNs(transaction.desiredPresentTime),
-                                                  transaction.originUid, transaction.states,
-                                                  bufferLayersReadyToPresent, transactions.size(),
-                                                  tryApplyUnsignaled);
-            ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
-            if (ready == TransactionReadiness::NotReady) {
-                setTransactionFlags(eTransactionFlushNeeded);
-                break;
-            }
-            if (ready == TransactionReadiness::NotReadyBarrier) {
-                transactionsPendingBarrier++;
-                setTransactionFlags(eTransactionFlushNeeded);
-                break;
-            }
-            transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
-                const bool frameNumberChanged = state.bufferData->flags.test(
-                        BufferData::BufferDataChange::frameNumberChanged);
-                if (frameNumberChanged) {
-                    bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
-                } else {
-                    // Barrier function only used for BBQ which always includes a frame number
-                    bufferLayersReadyToPresent[state.surface] =
-                        std::numeric_limits<uint64_t>::max();
-                }
-            });
-            const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
-            if (appliedUnsignaled) {
-                applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
-            }
-
-            transactions.emplace_back(std::move(transaction));
-            transactionQueue.pop();
-            mPendingTransactionCount--;
-            ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
-        }
-
-        if (transactionQueue.empty()) {
-            it = mPendingTransactionQueues.erase(it);
-        } else {
-            it = std::next(it, 1);
-        }
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
+        const TransactionHandler::TransactionFlushState& flushState) {
+    using TransactionReadiness = TransactionHandler::TransactionReadiness;
+    const auto& transaction = *flushState.transaction;
+    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64,
+                  transaction.frameTimelineInfo.vsyncId);
+    TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+    // 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.
+    if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
+        desiredPresentTime < mExpectedPresentTime + 1s) {
+        ATRACE_NAME("not current");
+        return TransactionReadiness::NotReady;
     }
-    return transactionsPendingBarrier;
+
+    if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
+        ATRACE_NAME("!isVsyncValid");
+        return TransactionReadiness::NotReady;
+    }
+
+    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the
+    // expected present time of this transaction.
+    if (transaction.isAutoTimestamp &&
+        frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
+        ATRACE_NAME("frameIsEarly");
+        return TransactionReadiness::NotReady;
+    }
+    return TransactionReadiness::Ready;
+}
+
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+        const TransactionHandler::TransactionFlushState& flushState) {
+    using TransactionReadiness = TransactionHandler::TransactionReadiness;
+    auto ready = TransactionReadiness::Ready;
+    flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool {
+        sp<Layer> layer = Layer::fromHandle(s.surface).promote();
+        const auto& transaction = *flushState.transaction;
+        // check for barrier frames
+        if (s.bufferData->hasBarrier &&
+            ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
+            const bool willApplyBarrierFrame =
+                    flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+                    (flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+                     s.bufferData->barrierFrameNumber);
+            if (!willApplyBarrierFrame) {
+                ATRACE_NAME("NotReadyBarrier");
+                ready = TransactionReadiness::NotReadyBarrier;
+                return false;
+            }
+        }
+
+        // If backpressure is enabled and we already have a buffer to commit, keep
+        // the transaction in the queue.
+        const bool hasPendingBuffer =
+                flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+        if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
+            ATRACE_NAME("hasPendingBuffer");
+            ready = TransactionReadiness::NotReady;
+            return false;
+        }
+
+        // check fence status
+        const bool allowLatchUnsignaled = shouldLatchUnsignaled(layer, s, transaction.states.size(),
+                                                                flushState.firstTransaction);
+        ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
+                      allowLatchUnsignaled ? "true" : "false");
+
+        const bool acquireFenceChanged = s.bufferData &&
+                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+                s.bufferData->acquireFence;
+        const bool fenceSignaled =
+                (!acquireFenceChanged ||
+                 s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled);
+        if (!fenceSignaled) {
+            if (!allowLatchUnsignaled) {
+                ready = TransactionReadiness::NotReady;
+                auto& listener = s.bufferData->releaseBufferListener;
+                if (listener &&
+                    (flushState.queueProcessTime - transaction.postTime) >
+                            std::chrono::nanoseconds(4s).count()) {
+                    mTransactionHandler.onTransactionQueueStalled(transaction, listener);
+                }
+                return false;
+            }
+
+            ready = enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer
+                    ? TransactionReadiness::ReadyUnsignaledSingle
+                    : TransactionReadiness::ReadyUnsignaled;
+        }
+        return true;
+    });
+    ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
+    return ready;
+}
+
+void SurfaceFlinger::addTransactionReadyFilters() {
+    mTransactionHandler.addTransactionReadyFilter(
+            std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
+    mTransactionHandler.addTransactionReadyFilter(
+            std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
 }
 
 bool SurfaceFlinger::flushTransactionQueues(VsyncId 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<TransactionState> transactions;
-    // Layer handles that have transactions with buffers that are ready to be applied.
-    std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
+    std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
     {
         Mutex::Autolock _l(mStateLock);
-        {
-            while (!mLocklessTransactionQueue.isEmpty()) {
-                auto maybeTransaction = mLocklessTransactionQueue.pop();
-                if (!maybeTransaction.has_value()) {
-                    break;
-                }
-                auto transaction = maybeTransaction.value();
-                mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
-            }
-
-            // Transactions with a buffer pending on a barrier may be on a different applyToken
-            // than the transaction which satisfies our barrier. In fact this is the exact use case
-            // that the primitive is designed for. This means we may first process
-            // the barrier dependent transaction, determine it ineligible to complete
-            // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
-            // The barrier dependent transaction was eligible to be presented in this frame
-            // but we would have prevented it without case. To fix this we continually
-            // loop through flushPendingTransactionQueues until we perform an iteration
-            // where the number of transactionsPendingBarrier doesn't change. This way
-            // we can continue to resolve dependency chains of barriers as far as possible.
-            int lastTransactionsPendingBarrier = 0;
-            int transactionsPendingBarrier = 0;
-            do {
-                lastTransactionsPendingBarrier = transactionsPendingBarrier;
-                transactionsPendingBarrier =
-                        flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                                                      /*tryApplyUnsignaled*/ false);
-            } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
-
-            // We collected all transactions that could apply without latching unsignaled buffers.
-            // If we are allowing latch unsignaled of some form, now it's the time to go over the
-            // transactions that were not applied and try to apply them unsignaled.
-            if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
-                flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                                              /*tryApplyUnsignaled*/ true);
-            }
-
-            return applyTransactions(transactions, vsyncId);
-        }
+        return applyTransactions(transactions, vsyncId);
     }
 }
 
@@ -3740,10 +3756,6 @@
                                       transaction.permissions, transaction.hasListenerCallbacks,
                                       transaction.listenerCallbacks, transaction.originPid,
                                       transaction.originUid, transaction.id);
-        if (transaction.transactionCommittedSignal) {
-            mTransactionCommittedSignals.emplace_back(
-                    std::move(transaction.transactionCommittedSignal));
-        }
     }
 
     if (mTransactionTracing) {
@@ -3753,7 +3765,7 @@
 }
 
 bool SurfaceFlinger::transactionFlushNeeded() {
-    return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+    return mTransactionHandler.hasPendingTransactions();
 }
 
 bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId) const {
@@ -3779,7 +3791,7 @@
 }
 
 bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
-                                           size_t numStates, size_t totalTXapplied) const {
+                                           size_t numStates, bool firstTransaction) const {
     if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
         ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
         return false;
@@ -3798,9 +3810,9 @@
     }
 
     if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
-        if (totalTXapplied > 0) {
-            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)",
-                  __func__, totalTXapplied);
+        if (!firstTransaction) {
+            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first transaction)",
+                  __func__);
             return false;
         }
 
@@ -3824,144 +3836,6 @@
     return true;
 }
 
-auto SurfaceFlinger::transactionIsReadyToBeApplied(
-        TransactionState& transaction, const FrameTimelineInfo& info, bool isAutoTimestamp,
-        TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>& states,
-        const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
-                bufferLayersReadyToPresent,
-        size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
-    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
-    // 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.
-    if (!isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
-        desiredPresentTime < mExpectedPresentTime + 1s) {
-        ATRACE_NAME("not current");
-        return TransactionReadiness::NotReady;
-    }
-
-    if (!mScheduler->isVsyncValid(mExpectedPresentTime, originUid)) {
-        ATRACE_NAME("!isVsyncValid");
-        return TransactionReadiness::NotReady;
-    }
-
-    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
-    // present time of this transaction.
-    if (isAutoTimestamp && frameIsEarly(mExpectedPresentTime, VsyncId{info.vsyncId})) {
-        ATRACE_NAME("frameIsEarly");
-        return TransactionReadiness::NotReady;
-    }
-
-    bool fenceUnsignaled = false;
-    auto queueProcessTime = systemTime();
-    for (const ComposerState& state : states) {
-        const layer_state_t& s = state.state;
-
-        sp<Layer> layer = nullptr;
-        if (s.surface) {
-            layer = fromHandle(s.surface).promote();
-        } else if (s.hasBufferChanges()) {
-            ALOGW("Transaction with buffer, but no Layer?");
-            continue;
-        }
-        if (!layer) {
-            continue;
-        }
-
-        if (s.hasBufferChanges() && s.bufferData->hasBarrier &&
-            ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
-            const bool willApplyBarrierFrame =
-                (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) &&
-                (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber);
-            if (!willApplyBarrierFrame) {
-                ATRACE_NAME("NotReadyBarrier");
-                return TransactionReadiness::NotReadyBarrier;
-            }
-        }
-
-        const bool allowLatchUnsignaled = tryApplyUnsignaled &&
-                shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
-        ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
-                      allowLatchUnsignaled ? "true" : "false");
-
-        const bool acquireFenceChanged = s.bufferData &&
-                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
-                s.bufferData->acquireFence;
-        fenceUnsignaled = fenceUnsignaled ||
-                (acquireFenceChanged &&
-                 s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled);
-
-        if (fenceUnsignaled && !allowLatchUnsignaled) {
-            if (!transaction.sentFenceTimeoutWarning &&
-                queueProcessTime - transaction.postTime > std::chrono::nanoseconds(4s).count()) {
-                transaction.sentFenceTimeoutWarning = true;
-                auto listener = s.bufferData->releaseBufferListener;
-                if (listener) {
-                    listener->onTransactionQueueStalled();
-                }
-            }
-
-            ATRACE_NAME("fence unsignaled");
-            return TransactionReadiness::NotReady;
-        }
-
-        if (s.hasBufferChanges()) {
-            // If backpressure is enabled and we already have a buffer to commit, keep the
-            // transaction in the queue.
-            const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) !=
-                bufferLayersReadyToPresent.end();
-            if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
-                ATRACE_NAME("hasPendingBuffer");
-                return TransactionReadiness::NotReady;
-            }
-        }
-    }
-    return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready;
-}
-
-void SurfaceFlinger::queueTransaction(TransactionState& state) {
-    // Generate a CountDownLatch pending state if this is a synchronous transaction.
-    if (state.flags & eSynchronous) {
-        state.transactionCommittedSignal =
-                std::make_shared<CountDownLatch>(CountDownLatch::eSyncTransaction);
-    }
-
-    mLocklessTransactionQueue.push(state);
-    mPendingTransactionCount++;
-    ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
-
-    const auto schedule = [](uint32_t flags) {
-        if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
-        if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
-        return TransactionSchedule::Late;
-    }(state.flags);
-
-    const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
-
-    setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
-}
-
-void SurfaceFlinger::waitForSynchronousTransaction(
-        const CountDownLatch& transactionCommittedSignal) {
-    // applyTransactionState is called on the main SF thread.  While a given process may wish
-    // to wait on synchronous transactions, the main SF thread should apply the transaction and
-    // set the value to notify this after committed.
-    if (!transactionCommittedSignal.wait_until(
-                std::chrono::nanoseconds(mAnimationTransactionTimeout))) {
-        ALOGE("setTransactionState timed out!");
-    }
-}
-
-void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) {
-    for (auto it = mTransactionCommittedSignals.begin();
-         it != mTransactionCommittedSignals.end();) {
-        if ((*it)->countDown(flag)) {
-            it = mTransactionCommittedSignals.erase(it);
-        } else {
-            it++;
-        }
-    }
-}
-
 status_t SurfaceFlinger::setTransactionState(
         const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
@@ -4012,12 +3886,16 @@
     if (mTransactionTracing) {
         mTransactionTracing->addQueuedTransaction(state);
     }
-    queueTransaction(state);
 
-    // Check the pending state to make sure the transaction is synchronous.
-    if (state.transactionCommittedSignal) {
-        waitForSynchronousTransaction(*state.transactionCommittedSignal);
-    }
+    const auto schedule = [](uint32_t flags) {
+        if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+        if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+        return TransactionSchedule::Late;
+    }(state.flags);
+
+    const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
+    setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
+    mTransactionHandler.queueTransaction(std::move(state));
 
     return NO_ERROR;
 }
@@ -4075,18 +3953,12 @@
     // anyway. This can be used as a flush mechanism for previous async transactions.
     // Empty animation transaction can be used to simulate back-pressure, so also force a
     // transaction for empty animation transactions.
-    if (transactionFlags == 0 &&
-            ((flags & eSynchronous) || (flags & eAnimation))) {
+    if (transactionFlags == 0 && (flags & eAnimation)) {
         transactionFlags = eTransactionNeeded;
     }
 
     bool needsTraversal = false;
     if (transactionFlags) {
-        if (mInterceptor->isEnabled()) {
-            mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
-                                          originPid, originUid, transactionId);
-        }
-
         // We are on the main thread, we are about to preform a traversal. Clear the traversal bit
         // so we don't have to wake up again next frame to preform an unnecessary traversal.
         if (transactionFlags & eTraversalNeeded) {
@@ -4258,18 +4130,11 @@
             }
         }
     }
-    if (what & layer_state_t::eSizeChanged) {
-        if (layer->setSize(s.w, s.h)) {
-            flags |= eTraversalNeeded;
-        }
-    }
     if (what & layer_state_t::eAlphaChanged) {
-        if (layer->setAlpha(s.alpha))
-            flags |= eTraversalNeeded;
+        if (layer->setAlpha(s.color.a)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eColorChanged) {
-        if (layer->setColor(s.color))
-            flags |= eTraversalNeeded;
+        if (layer->setColor(s.color.rgb)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eColorTransformChanged) {
         if (layer->setColorTransform(s.colorTransform)) {
@@ -4277,7 +4142,7 @@
         }
     }
     if (what & layer_state_t::eBackgroundColorChanged) {
-        if (layer->setBackgroundColor(s.color, s.bgColorAlpha, s.bgColorDataspace)) {
+        if (layer->setBackgroundColor(s.color.rgb, s.bgColorAlpha, s.bgColorDataspace)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -4326,8 +4191,8 @@
             flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
         }
     }
-    if (what & layer_state_t::eTransformChanged) {
-        if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
+    if (what & layer_state_t::eBufferTransformChanged) {
+        if (layer->setTransform(s.bufferTransform)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eTransformToDisplayInverseChanged) {
         if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse))
@@ -4367,7 +4232,10 @@
             layer->setGameModeForTree(static_cast<GameMode>(gameMode));
         }
 
-        if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
+        if (layer->setMetadata(s.metadata)) {
+            flags |= eTraversalNeeded;
+            mLayerMetadataSnapshotNeeded = true;
+        }
     }
     if (what & layer_state_t::eColorSpaceAgnosticChanged) {
         if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) {
@@ -4486,8 +4354,8 @@
 }
 
 status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args,
-                                     const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
-                                     int32_t* outLayerId) {
+                                     const sp<IBinder>& mirrorFromHandle,
+                                     gui::CreateSurfaceResult& outResult) {
     if (!mirrorFromHandle) {
         return NAME_NOT_FOUND;
     }
@@ -4502,7 +4370,7 @@
         }
         LayerCreationArgs mirrorArgs = args;
         mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
-        status_t result = createEffectLayer(mirrorArgs, outHandle, &mirrorLayer);
+        status_t result = createEffectLayer(mirrorArgs, &outResult.handle, &mirrorLayer);
         if (result != NO_ERROR) {
             return result;
         }
@@ -4510,17 +4378,20 @@
         mirrorLayer->setClonedChild(mirrorFrom->createClone());
     }
 
-    *outLayerId = mirrorLayer->sequence;
+    outResult.layerId = mirrorLayer->sequence;
+    outResult.layerName = String16(mirrorLayer->getDebugName());
     if (mTransactionTracing) {
-        mTransactionTracing->onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
-                                                args.name, mirrorFrom->sequence);
+        mTransactionTracing->onMirrorLayerAdded(outResult.handle->localBinder(),
+                                                mirrorLayer->sequence, args.name,
+                                                mirrorFrom->sequence);
     }
-    return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */,
-                          false /* addToRoot */, nullptr /* outTransformHint */);
+    return addClientLayer(args.client, outResult.handle, mirrorLayer /* layer */,
+                          nullptr /* parent */, false /* addToRoot */,
+                          nullptr /* outTransformHint */);
 }
 
 status_t SurfaceFlinger::mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
-                                       sp<IBinder>* outHandle, int32_t* outLayerId) {
+                                       gui::CreateSurfaceResult& outResult) {
     IPCThreadState* ipc = IPCThreadState::self();
     const int uid = ipc->getCallingUid();
     if (uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != AID_SHELL) {
@@ -4543,9 +4414,10 @@
         layerStack = display->getLayerStack();
         LayerCreationArgs mirrorArgs = args;
         mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
-        result = createEffectLayer(mirrorArgs, outHandle, &rootMirrorLayer);
-        *outLayerId = rootMirrorLayer->sequence;
-        result |= addClientLayer(args.client, *outHandle, rootMirrorLayer /* layer */,
+        result = createEffectLayer(mirrorArgs, &outResult.handle, &rootMirrorLayer);
+        outResult.layerId = rootMirrorLayer->sequence;
+        outResult.layerName = String16(rootMirrorLayer->getDebugName());
+        result |= addClientLayer(args.client, outResult.handle, rootMirrorLayer /* layer */,
                                  nullptr /* parent */, true /* addToRoot */,
                                  nullptr /* outTransformHint */);
     }
@@ -4555,47 +4427,40 @@
     }
 
     if (mTransactionTracing) {
-        mTransactionTracing->onLayerAdded((*outHandle)->localBinder(), *outLayerId, args.name,
-                                          args.flags, -1 /* parentId */);
+        mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), outResult.layerId,
+                                          args.name, args.flags, -1 /* parentId */);
     }
 
     {
         std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
-        mMirrorDisplays.emplace_back(layerStack, *outHandle, args.client);
+        mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client);
     }
 
     setTransactionFlags(eTransactionFlushNeeded);
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
-                                     const sp<IBinder>& parentHandle, int32_t* outLayerId,
-                                     const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
-    ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
-            "Expected only one of parentLayer or parentHandle to be non-null. "
-            "Programmer error?");
-
+status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
+                                     gui::CreateSurfaceResult& outResult) {
     status_t result = NO_ERROR;
 
     sp<Layer> layer;
 
     switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-        case ISurfaceComposerClient::eFXSurfaceBufferState: {
-            result = createBufferStateLayer(args, outHandle, &layer);
+        case ISurfaceComposerClient::eFXSurfaceContainer:
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            args.flags |= ISurfaceComposerClient::eNoColorFill;
+            FMT_FALLTHROUGH;
+        case ISurfaceComposerClient::eFXSurfaceEffect: {
+            result = createBufferStateLayer(args, &outResult.handle, &layer);
             std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
             if (pendingBufferCounter) {
                 std::string counterName = layer->getPendingBufferCounterName();
-                mBufferCountTracker.add((*outHandle)->localBinder(), counterName,
+                mBufferCountTracker.add(outResult.handle->localBinder(), counterName,
                                         pendingBufferCounter);
             }
         } break;
-        case ISurfaceComposerClient::eFXSurfaceContainer:
-            args.flags |= ISurfaceComposerClient::eNoColorFill;
-            FMT_FALLTHROUGH;
-        case ISurfaceComposerClient::eFXSurfaceEffect:
-            result = createEffectLayer(args, outHandle, &layer);
-            break;
         default:
             result = BAD_VALUE;
             break;
@@ -4606,14 +4471,11 @@
     }
 
     bool addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
-    wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer);
+    wp<Layer> parent = fromHandle(parentHandle);
     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
@@ -4623,16 +4485,20 @@
         parentId = parentSp->getSequence();
     }
     if (mTransactionTracing) {
-        mTransactionTracing->onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
-                                          args.flags, parentId);
+        mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), layer->sequence,
+                                          args.name, args.flags, parentId);
     }
 
-    result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint);
+    uint32_t outTransformHint;
+    result = addClientLayer(args.client, outResult.handle, layer, parent, addToRoot,
+                            &outTransformHint);
     if (result != NO_ERROR) {
         return result;
     }
 
-    *outLayerId = layer->sequence;
+    outResult.transformHint = static_cast<int32_t>(outTransformHint);
+    outResult.layerId = layer->sequence;
+    outResult.layerName = String16(layer->getDebugName());
     return result;
 }
 
@@ -4667,8 +4533,6 @@
     }
 }
 
-// ---------------------------------------------------------------------------
-
 void SurfaceFlinger::onInitializeDisplays() {
     const auto display = getDefaultDisplayDeviceLocked();
     if (!display) return;
@@ -4706,8 +4570,9 @@
 
 void SurfaceFlinger::initializeDisplays() {
     // Async since we may be called from the main thread.
-    static_cast<void>(
-            mScheduler->schedule([this]() FTL_FAKE_GUARD(mStateLock) { onInitializeDisplays(); }));
+    static_cast<void>(mScheduler->schedule(
+            [this]() FTL_FAKE_GUARD(mStateLock)
+                    FTL_FAKE_GUARD(kMainThreadContext) { onInitializeDisplays(); }));
 }
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4719,26 +4584,28 @@
     const auto displayId = display->getPhysicalId();
     ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
 
-    const hal::PowerMode currentMode = display->getPowerMode();
-    if (mode == currentMode) {
+    std::optional<hal::PowerMode> currentMode = display->getPowerMode();
+    if (currentMode.has_value() && mode == *currentMode) {
         return;
     }
 
-    const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken);
-    if (activeDisplay != display && display->isInternal() && activeDisplay &&
+    const bool isActiveDisplay = displayId == mActiveDisplayId;
+    const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+                                           .transform(&PhysicalDisplay::isInternal)
+                                           .value_or(false);
+
+    const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId);
+    if (isInternalDisplay && activeDisplay != display && activeDisplay &&
         activeDisplay->isPoweredOn()) {
         ALOGW("Trying to change power mode on non active display while the active display is ON");
     }
 
     display->setPowerMode(mode);
 
-    if (mInterceptor->isEnabled()) {
-        mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
-    }
-    const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
-    if (currentMode == hal::PowerMode::OFF) {
+    const auto refreshRate = display->refreshRateConfigs().getActiveMode().getFps();
+    if (*currentMode == hal::PowerMode::OFF) {
         // Turn on the display
-        if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
+        if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
             onActiveDisplayChangedLocked(display);
         }
         // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
@@ -4750,14 +4617,13 @@
             ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
         }
         getHwComposer().setPowerMode(displayId, mode);
-        if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) {
+        if (isActiveDisplay && mode != hal::PowerMode::DOZE_SUSPEND) {
             setHWCVsyncEnabled(displayId, mHWCVsyncPendingState);
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, refreshRate);
         }
 
         mVisibleRegionsDirty = true;
-        mHasPoweredOff = true;
         scheduleComposite(FrameHint::kActive);
     } else if (mode == hal::PowerMode::OFF) {
         // Turn off the display
@@ -4767,7 +4633,7 @@
         if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
             ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
         }
-        if (isDisplayActiveLocked(display) && currentMode != hal::PowerMode::DOZE_SUSPEND) {
+        if (isActiveDisplay && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4781,7 +4647,7 @@
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
         getHwComposer().setPowerMode(displayId, mode);
-        if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) {
+        if (isActiveDisplay && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
             ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
             mVisibleRegionsDirty = true;
             scheduleRepaint();
@@ -4790,7 +4656,7 @@
         }
     } else if (mode == hal::PowerMode::DOZE_SUSPEND) {
         // Leave display going to doze
-        if (isDisplayActiveLocked(display)) {
+        if (isActiveDisplay) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4800,7 +4666,7 @@
         getHwComposer().setPowerMode(displayId, mode);
     }
 
-    if (isDisplayActiveLocked(display)) {
+    if (isActiveDisplay) {
         mTimeStats->setPowerMode(mode);
         mRefreshRateStats->setPowerMode(mode);
         mScheduler->setDisplayPowerMode(mode);
@@ -4810,7 +4676,8 @@
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+                                               kMainThreadContext) {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4847,15 +4714,34 @@
                 {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
                 {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
                 {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
-                {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
                 {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
                 {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
                 {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
                 {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
+                {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
         };
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
+        // Traversal of drawing state must happen on the main thread.
+        // Otherwise, SortedVector may have shared ownership during concurrent
+        // traversals, which can result in use-after-frees.
+        std::string compositionLayers;
+        mScheduler
+                ->schedule([&] {
+                    StringAppendF(&compositionLayers, "Composition layers\n");
+                    mDrawingState.traverseInZOrder([&](Layer* layer) {
+                        auto* compositionState = layer->getCompositionState();
+                        if (!compositionState || !compositionState->isVisible) return;
+
+                        android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer,
+                                                     layer->getDebugName() ? layer->getDebugName()
+                                                                           : "<unknown>");
+                        compositionState->dump(compositionLayers);
+                    });
+                })
+                .get();
+
         bool dumpLayers = true;
         {
             TimedLock lock(mStateLock, s2ns(1), __func__);
@@ -4868,7 +4754,7 @@
                 (it->second)(args, asProto, result);
                 dumpLayers = false;
             } else if (!asProto) {
-                dumpAllLocked(args, result);
+                dumpAllLocked(args, compositionLayers, result);
             }
         }
 
@@ -4990,21 +4876,6 @@
     }
 }
 
-void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
-    result.append("Static screen stats:\n");
-    for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
-        float bucketTimeSec = getBE().mFrameBuckets[b] / 1e9;
-        float percent = 100.0f *
-                static_cast<float>(getBE().mFrameBuckets[b]) / getBE().mTotalTime;
-        StringAppendF(&result, "  < %zd frames: %.3f s (%.1f%%)\n", b + 1, bucketTimeSec, percent);
-    }
-    float bucketTimeSec = getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] / 1e9;
-    float percent = 100.0f *
-            static_cast<float>(getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1]) / getBE().mTotalTime;
-    StringAppendF(&result, "  %zd+ frames: %.3f s (%.1f%%)\n", SurfaceFlingerBE::NUM_BUCKETS - 1,
-                  bucketTimeSec, percent);
-}
-
 void SurfaceFlinger::dumpCompositionDisplays(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
         display->getCompositionDisplay()->dump(result);
@@ -5013,9 +4884,23 @@
 }
 
 void SurfaceFlinger::dumpDisplays(std::string& result) const {
+    utils::Dumper dumper{result};
+
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        if (const auto device = getDisplayDeviceLocked(id)) {
+            device->dump(dumper);
+        }
+
+        utils::Dumper::Indent indent(dumper);
+        display.snapshot().dump(dumper);
+        dumper.eol();
+    }
+
     for (const auto& [token, display] : mDisplays) {
-        display->dump(result);
-        result += '\n';
+        if (display->isVirtual()) {
+            display->dump(dumper);
+            dumper.eol();
+        }
     }
 }
 
@@ -5070,28 +4955,24 @@
 }
 
 void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
-    StringAppendF(&result, "Device has wide color built-in display: %d\n", hasWideColorDisplay);
+    StringAppendF(&result, "Device supports wide color: %d\n", mSupportsWideColor);
     StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
     StringAppendF(&result, "DisplayColorSetting: %s\n",
                   decodeDisplayColorSetting(mDisplayColorSetting).c_str());
 
     // TODO: print out if wide-color mode is active or not
 
-    for (const auto& [token, display] : mDisplays) {
-        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
-        if (!displayId) {
-            continue;
-        }
-
-        StringAppendF(&result, "Display %s color modes:\n", to_string(*displayId).c_str());
-        std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
-        for (auto&& mode : modes) {
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        StringAppendF(&result, "Display %s color modes:\n", to_string(id).c_str());
+        for (const auto mode : display.snapshot().colorModes()) {
             StringAppendF(&result, "    %s (%d)\n", decodeColorMode(mode).c_str(), mode);
         }
 
-        ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
-        StringAppendF(&result, "    Current color mode: %s (%d)\n",
-                      decodeColorMode(currentMode).c_str(), currentMode);
+        if (const auto display = getDisplayDeviceLocked(id)) {
+            ui::ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
+            StringAppendF(&result, "    Current color mode: %s (%d)\n",
+                          decodeColorMode(currentMode).c_str(), currentMode);
+        }
     }
     result.append("\n");
 }
@@ -5163,7 +5044,25 @@
     result.append(future.get());
 }
 
-void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const {
+    for (const auto& [token, display] : mDisplays) {
+        const auto displayId = HalDisplayId::tryCast(display->getId());
+        if (!displayId) {
+            continue;
+        }
+
+        StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
+                      displayId == mActiveDisplayId ? "active" : "inactive");
+        Layer::miniDumpHeader(result);
+
+        const DisplayDevice& ref = *display;
+        mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
+        result.append("\n");
+    }
+}
+
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
+                                   std::string& result) const {
     const bool colorize = !args.empty() && args[0] == String16("--color");
     Colorizer colorizer(colorize);
 
@@ -5200,9 +5099,6 @@
     dumpVSync(result);
     result.append("\n");
 
-    dumpStaticScreenStats(result);
-    result.append("\n");
-
     StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load());
     StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load());
     StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load());
@@ -5214,18 +5110,7 @@
     StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
     colorizer.reset(result);
 
-    {
-        StringAppendF(&result, "Composition layers\n");
-        mDrawingState.traverseInZOrder([&](Layer* layer) {
-            auto* compositionState = layer->getCompositionState();
-            if (!compositionState || !compositionState->isVisible) return;
-
-            android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
-                                         layer->getDebugName() ? layer->getDebugName()
-                                                               : "<unknown>");
-            compositionState->dump(result);
-        });
-    }
+    result.append(compositionLayers);
 
     colorizer.bold(result);
     StringAppendF(&result, "Displays (%zu entries)\n", mDisplays.size());
@@ -5263,10 +5148,10 @@
 
     if (const auto display = getDefaultDisplayDeviceLocked()) {
         std::string fps, xDpi, yDpi;
-        if (const auto activeMode = display->getActiveMode()) {
-            fps = to_string(activeMode->getFps());
+        if (const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr()) {
+            fps = to_string(activeModePtr->getFps());
 
-            const auto dpi = activeMode->getDpi();
+            const auto dpi = activeModePtr->getDpi();
             xDpi = base::StringPrintf("%.2f", dpi.x);
             yDpi = base::StringPrintf("%.2f", dpi.y);
         } else {
@@ -5297,23 +5182,7 @@
     }
     result.push_back('\n');
 
-    /*
-     * HWC layer minidump
-     */
-    for (const auto& [token, display] : mDisplays) {
-        const auto displayId = HalDisplayId::tryCast(display->getId());
-        if (!displayId) {
-            continue;
-        }
-
-        StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
-                      (isDisplayActiveLocked(display) ? "active" : "inactive"));
-        Layer::miniDumpHeader(result);
-
-        const DisplayDevice& ref = *display;
-        mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
-        result.append("\n");
-    }
+    dumpHwcLayersMinidumpLocked(result);
 
     {
         DumpArgs plannerArgs;
@@ -5622,17 +5491,8 @@
                 mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns);
                 return NO_ERROR;
             }
-            case 1020: { // Layer updates interceptor
-                n = data.readInt32();
-                if (n) {
-                    ALOGV("Interceptor enabled");
-                    mInterceptor->enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
-                }
-                else{
-                    ALOGV("Interceptor disabled");
-                    mInterceptor->disable();
-                }
-                return NO_ERROR;
+            case 1020: { // Unused
+                return NAME_NOT_FOUND;
             }
             case 1021: { // Disable HWC virtual displays
                 const bool enable = data.readInt32() != 0;
@@ -5647,12 +5507,11 @@
                 updateColorMatrixLocked();
                 return NO_ERROR;
             }
-            case 1023: { // Set native mode
-                int32_t colorMode;
-
+            case 1023: { // Set color mode.
                 mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32());
-                if (data.readInt32(&colorMode) == NO_ERROR) {
-                    mForceColorMode = static_cast<ColorMode>(colorMode);
+
+                if (int32_t colorMode; data.readInt32(&colorMode) == NO_ERROR) {
+                    mForceColorMode = static_cast<ui::ColorMode>(colorMode);
                 }
                 scheduleRepaint();
                 return NO_ERROR;
@@ -5782,19 +5641,17 @@
                 return NO_ERROR;
             }
             case 1034: {
-                auto future = mScheduler->schedule([&] {
-                    switch (n = data.readInt32()) {
-                        case 0:
-                        case 1:
-                            FTL_FAKE_GUARD(mStateLock,
-                                           enableRefreshRateOverlay(static_cast<bool>(n)));
-                            break;
-                        default: {
-                            reply->writeBool(
-                                    FTL_FAKE_GUARD(mStateLock, isRefreshRateOverlayEnabled()));
-                        }
-                    }
-                });
+                auto future = mScheduler->schedule(
+                        [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+                            switch (n = data.readInt32()) {
+                                case 0:
+                                case 1:
+                                    enableRefreshRateOverlay(static_cast<bool>(n));
+                                    break;
+                                default:
+                                    reply->writeBool(isRefreshRateOverlayEnabled());
+                            }
+                        });
 
                 future.wait();
                 return NO_ERROR;
@@ -5817,7 +5674,7 @@
                 }();
 
                 mDebugDisplayModeSetByBackdoor = false;
-                const status_t result = setActiveModeFromBackdoor(display, modeId);
+                const status_t result = setActiveModeFromBackdoor(display, DisplayModeId{modeId});
                 mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
                 return result;
             }
@@ -5827,7 +5684,7 @@
             case 1036: {
                 if (data.readInt32() > 0) { // turn on
                     return mScheduler
-                            ->schedule([this] {
+                            ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
                                 const auto display =
                                         FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
 
@@ -5837,24 +5694,21 @@
                                 // 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;
+                                scheduler::RefreshRateConfigs::OverridePolicy overridePolicy;
                                 overridePolicy.defaultMode = display->refreshRateConfigs()
                                                                      .getDisplayManagerPolicy()
                                                                      .defaultMode;
                                 overridePolicy.allowGroupSwitching = true;
-                                constexpr bool kOverridePolicy = true;
-                                return setDesiredDisplayModeSpecsInternal(display, overridePolicy,
-                                                                          kOverridePolicy);
+                                return setDesiredDisplayModeSpecsInternal(display, overridePolicy);
                             })
                             .get();
                 } else { // turn off
                     return mScheduler
-                            ->schedule([this] {
+                            ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
                                 const auto display =
                                         FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
-                                constexpr bool kOverridePolicy = true;
-                                return setDesiredDisplayModeSpecsInternal(display, {},
-                                                                          kOverridePolicy);
+                                return setDesiredDisplayModeSpecsInternal(
+                                        display, scheduler::RefreshRateConfigs::NoOverridePolicy{});
                             })
                             .get();
                 }
@@ -6083,18 +5937,6 @@
     const int mApi;
 };
 
-static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
-    switch (colorMode) {
-        case ColorMode::DISPLAY_P3:
-        case ColorMode::BT2100_PQ:
-        case ColorMode::BT2100_HLG:
-        case ColorMode::DISPLAY_BT2020:
-            return Dataspace::DISPLAY_P3;
-        default:
-            return Dataspace::V0_SRGB;
-    }
-}
-
 static bool hasCaptureBlackoutContentPermission() {
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
@@ -6206,15 +6048,11 @@
             reqSize = display->getLayerStackSpaceRect().getSize();
         }
 
-        // The dataspace is depended on the color mode of display, that could use non-native mode
-        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
-        // and failed if display is not in native mode. This provide a way to force using native
-        // colors when capture.
-        dataspace = args.dataspace;
-        if (dataspace == ui::Dataspace::UNKNOWN) {
-            const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
-            dataspace = pickDataspaceFromColorMode(colorMode);
-        }
+        // Allow the caller to specify a dataspace regardless of the display's color mode, e.g. if
+        // it wants sRGB regardless of the display's wide color mode.
+        dataspace = args.dataspace == ui::Dataspace::UNKNOWN
+                ? ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode)
+                : args.dataspace;
     }
 
     RenderAreaFuture renderAreaFuture = ftl::defer([=] {
@@ -6249,9 +6087,7 @@
         displayWeak = display;
         layerStack = display->getLayerStack();
         size = display->getLayerStackSpaceRect().getSize();
-
-        dataspace =
-                pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
+        dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode);
     }
 
     RenderAreaFuture renderAreaFuture = ftl::defer([=] {
@@ -6464,7 +6300,7 @@
         std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
         if (!renderArea) {
             ALOGW("Skipping screen capture because of invalid render area.");
-            captureResults.result = NO_MEMORY;
+            captureResults.fenceResult = base::unexpected(NO_MEMORY);
             captureListener->onScreenCaptureCompleted(captureResults);
             return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
         }
@@ -6481,9 +6317,7 @@
             return ftl::Future(std::move(renderFuture))
                     .then([captureListener, captureResults = std::move(captureResults)](
                                   FenceResult fenceResult) mutable -> FenceResult {
-                        // TODO(b/232535621): Change ScreenCaptureResults to store a FenceResult.
-                        captureResults.result = fenceStatus(fenceResult);
-                        captureResults.fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
+                        captureResults.fenceResult = std::move(fenceResult);
                         captureListener->onScreenCaptureCompleted(captureResults);
                         return base::unexpected(NO_ERROR);
                     })
@@ -6529,7 +6363,7 @@
     auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance;
     auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance;
 
-    if ((dataspace == ui::Dataspace::UNKNOWN) && (parent != nullptr)) {
+    if (dataspace == ui::Dataspace::UNKNOWN && parent) {
         Mutex::Autolock lock(mStateLock);
         auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
             return display.getLayerStack() == layerStack;
@@ -6539,8 +6373,7 @@
             display = getDefaultDisplayDeviceLocked();
         }
 
-        const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
-        dataspace = pickDataspaceFromColorMode(colorMode);
+        dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode);
         renderIntent = display->getCompositionDisplay()->getState().renderIntent;
         sdrWhitePointNits = display->getCompositionDisplay()->getState().sdrWhitePointNits;
         displayBrightnessNits = display->getCompositionDisplay()->getState().displayBrightnessNits;
@@ -6586,6 +6419,11 @@
     std::vector<Layer*> renderedLayers;
     bool disableBlurs = false;
     traverseLayers([&](Layer* layer) {
+        // Layer::prepareClientComposition uses the layer's snapshot to populate the resulting
+        // LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are
+        // generated with the layer's current buffer and geometry.
+        layer->updateSnapshot(true /* updateGeometry */);
+
         disableBlurs |= layer->getDrawingState().sidebandStream != nullptr;
 
         Region clip(renderArea.getBounds());
@@ -6606,27 +6444,25 @@
                 isHdrLayer(layer) ? displayBrightnessNits : sdrWhitePointNits,
 
         };
-        std::vector<compositionengine::LayerFE::LayerSettings> results =
-                layer->prepareClientCompositionList(targetSettings);
-        if (results.size() > 0) {
-            for (auto& settings : results) {
-                settings.geometry.positionTransform =
-                        transform.asMatrix4() * settings.geometry.positionTransform;
-                // There's no need to process blurs when we're executing region sampling,
-                // we're just trying to understand what we're drawing, and doing so without
-                // blurs is already a pretty good approximation.
-                if (regionSampling) {
-                    settings.backgroundBlurRadius = 0;
-                }
-                captureResults.capturedHdrLayers |= isHdrLayer(layer);
-            }
-
-            clientCompositionLayers.insert(clientCompositionLayers.end(),
-                                           std::make_move_iterator(results.begin()),
-                                           std::make_move_iterator(results.end()));
-            renderedLayers.push_back(layer);
+        std::optional<compositionengine::LayerFE::LayerSettings> settings =
+                layer->prepareClientComposition(targetSettings);
+        if (!settings) {
+            return;
         }
 
+        settings->geometry.positionTransform =
+                transform.asMatrix4() * settings->geometry.positionTransform;
+        // There's no need to process blurs when we're executing region sampling,
+        // we're just trying to understand what we're drawing, and doing so without
+        // blurs is already a pretty good approximation.
+        if (regionSampling) {
+            settings->backgroundBlurRadius = 0;
+            settings->blurRegions.clear();
+        }
+        captureResults.capturedHdrLayers |= isHdrLayer(layer);
+
+        clientCompositionLayers.push_back(std::move(*settings));
+        renderedLayers.push_back(layer);
     });
 
     std::vector<renderengine::LayerSettings> clientRenderEngineLayers;
@@ -6642,13 +6478,11 @@
     getRenderEngine().useProtectedContext(useProtected);
 
     constexpr bool kUseFramebufferCache = false;
-    auto chain =
-            ftl::Future(getRenderEngine().drawLayers(clientCompositionDisplay,
-                                                     clientRenderEngineLayers, buffer,
-                                                     kUseFramebufferCache, std::move(bufferFence)))
-                    .then(&toFenceResult);
+    const auto future = getRenderEngine()
+                                .drawLayers(clientCompositionDisplay, clientRenderEngineLayers,
+                                            buffer, kUseFramebufferCache, std::move(bufferFence))
+                                .share();
 
-    const auto future = chain.share();
     for (auto* layer : renderedLayers) {
         layer->onLayerDisplayed(future);
     }
@@ -6697,9 +6531,25 @@
     }
 }
 
+std::optional<DisplayModePtr> SurfaceFlinger::getPreferredDisplayMode(
+        PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
+    if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
+        schedulerMode && schedulerMode->getPhysicalDisplayId() == displayId) {
+        return schedulerMode;
+    }
+
+    return mPhysicalDisplays.get(displayId)
+            .transform(&PhysicalDisplay::snapshotRef)
+            .and_then([&](const display::DisplaySnapshot& snapshot) {
+                return snapshot.displayModes().get(defaultModeId);
+            });
+}
+
 status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
         const sp<DisplayDevice>& display,
-        const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
+        const scheduler::RefreshRateConfigs::PolicyVariant& policy) {
+    const auto displayId = display->getPhysicalId();
+
     Mutex::Autolock lock(mStateLock);
 
     if (mDebugDisplayModeSetByBackdoor) {
@@ -6707,50 +6557,48 @@
         return NO_ERROR;
     }
 
-    const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy);
-    if (setPolicyResult < 0) {
-        return BAD_VALUE;
-    }
-    if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) {
-        return NO_ERROR;
+    auto& configs = display->refreshRateConfigs();
+    using SetPolicyResult = scheduler::RefreshRateConfigs::SetPolicyResult;
+
+    switch (configs.setPolicy(policy)) {
+        case SetPolicyResult::Invalid:
+            return BAD_VALUE;
+        case SetPolicyResult::Unchanged:
+            return NO_ERROR;
+        case SetPolicyResult::Changed:
+            break;
     }
 
-    scheduler::RefreshRateConfigs::Policy currentPolicy =
-            display->refreshRateConfigs().getCurrentPolicy();
-
+    const scheduler::RefreshRateConfigs::Policy currentPolicy = configs.getCurrentPolicy();
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
-    const auto activeMode = display->getActiveMode();
-    if (isDisplayActiveLocked(display)) {
-        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
+    if (const auto activeModePtr = configs.getActiveModePtr(); displayId == mActiveDisplayId) {
+        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
         toggleKernelIdleTimer();
     } else {
-        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
+        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
     }
 
-    const DisplayModePtr preferredDisplayMode = [&] {
-        const auto schedulerMode = mScheduler->getPreferredDisplayMode();
-        if (schedulerMode && schedulerMode->getPhysicalDisplayId() == display->getPhysicalId()) {
-            return schedulerMode;
-        }
-
-        return display->getMode(currentPolicy.defaultMode);
-    }();
-
-    ALOGV("trying to switch to Scheduler preferred mode %d (%s)",
-          preferredDisplayMode->getId().value(), to_string(preferredDisplayMode->getFps()).c_str());
-
-    if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
-        ALOGV("switching to Scheduler preferred display mode %d",
-              preferredDisplayMode->getId().value());
-        setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed});
-    } else {
-        LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
-                         preferredDisplayMode->getId().value());
+    auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
+    if (!preferredModeOpt) {
+        ALOGE("%s: Preferred mode is unknown", __func__);
+        return NAME_NOT_FOUND;
     }
 
+    auto preferredMode = std::move(*preferredModeOpt);
+    const auto preferredModeId = preferredMode->getId();
+
+    ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
+          to_string(preferredMode->getFps()).c_str());
+
+    if (!configs.isModeAllowed(preferredModeId)) {
+        ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
+        return INVALID_OPERATION;
+    }
+
+    setDesiredActiveMode({std::move(preferredMode), DisplayModeEvent::Changed});
     return NO_ERROR;
 }
 
@@ -6764,7 +6612,7 @@
         return BAD_VALUE;
     }
 
-    auto future = mScheduler->schedule([=]() -> status_t {
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
         const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
         if (!display) {
             ALOGE("Attempt to set desired display modes for invalid display token %p",
@@ -6774,16 +6622,15 @@
             ALOGW("Attempt to set desired display modes for virtual display");
             return INVALID_OPERATION;
         } else {
-            using Policy = scheduler::RefreshRateConfigs::Policy;
+            using Policy = scheduler::RefreshRateConfigs::DisplayManagerPolicy;
             const Policy policy{DisplayModeId(defaultMode),
                                 allowGroupSwitching,
                                 {Fps::fromValue(primaryRefreshRateMin),
                                  Fps::fromValue(primaryRefreshRateMax)},
                                 {Fps::fromValue(appRequestRefreshRateMin),
                                  Fps::fromValue(appRequestRefreshRateMax)}};
-            constexpr bool kOverridePolicy = false;
 
-            return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
+            return setDesiredDisplayModeSpecsInternal(display, policy);
         }
     });
 
@@ -6909,24 +6756,15 @@
 }
 
 void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
-    for (const auto& [ignored, display] : mDisplays) {
-        if (display->isInternal()) {
-            display->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+            if (const auto device = getDisplayDeviceLocked(id)) {
+                device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+            }
         }
     }
 }
 
-status_t SurfaceFlinger::addTransactionTraceListener(
-        const sp<gui::ITransactionTraceListener>& listener) {
-    if (!listener) {
-        return BAD_VALUE;
-    }
-
-    mInterceptor->addTransactionTraceListener(listener);
-
-    return NO_ERROR;
-}
-
 int SurfaceFlinger::getGpuContextPriority() {
     return getRenderEngine().getContextPriority();
 }
@@ -6960,7 +6798,7 @@
         refreshRate = *frameRateOverride;
     } else if (!getHwComposer().isHeadless()) {
         if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) {
-            refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
+            refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps();
         }
     }
 
@@ -7006,7 +6844,6 @@
     if (mTransactionTracing) {
         mTransactionTracing->onLayerAddedToDrawingState(layer->getSequence(), vsyncId.value);
     }
-    mInterceptor->saveSurfaceCreation(layer);
 }
 
 void SurfaceFlinger::sample() {
@@ -7017,7 +6854,7 @@
     mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
 }
 
-void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<const DisplayDevice>& activeDisplay) {
     mScheduler->onActiveDisplayAreaChanged(activeDisplay->getWidth() * activeDisplay->getHeight());
     getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
 }
@@ -7025,7 +6862,7 @@
 void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
     ATRACE_CALL();
 
-    if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+    if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
         display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
     }
 
@@ -7033,7 +6870,7 @@
         ALOGE("%s: activeDisplay is null", __func__);
         return;
     }
-    mActiveDisplayToken = activeDisplay->getDisplayToken();
+    mActiveDisplayId = activeDisplay->getPhysicalId();
     activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
     updateInternalDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
@@ -7148,16 +6985,29 @@
     return true;
 }
 
-void SurfaceFlinger::startPowerHintSession() const {
-    std::optional<pid_t> renderEngineTid = getRenderEngine().getRenderEngineTid();
-    std::vector<int32_t> tidList;
-    tidList.emplace_back(gettid());
-    if (renderEngineTid.has_value()) {
-        tidList.emplace_back(*renderEngineTid);
+void SurfaceFlinger::updateLayerMetadataSnapshot() {
+    LayerMetadata parentMetadata;
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
+        layer->updateMetadataSnapshot(parentMetadata);
     }
-    if (!mPowerAdvisor->startPowerHintSession(tidList)) {
-        ALOGW("Cannot start power hint session");
-    }
+
+    std::unordered_set<Layer*> visited;
+    mDrawingState.traverse([&visited](Layer* layer) {
+        if (visited.find(layer) != visited.end()) {
+            return;
+        }
+
+        // If the layer isRelativeOf, then either it's relative metadata will be set
+        // recursively when updateRelativeMetadataSnapshot is called on its relative parent or
+        // it's relative parent has been deleted. Clear the layer's relativeLayerMetadata to ensure
+        // that layers with deleted relative parents don't hold stale relativeLayerMetadata.
+        if (layer->getDrawingState().isRelativeOf) {
+            layer->editLayerSnapshot()->relativeLayerMetadata = {};
+            return;
+        }
+
+        layer->updateRelativeMetadataSnapshot({}, visited);
+    });
 }
 
 // gui::ISurfaceComposer
@@ -7409,8 +7259,7 @@
                                                        int displayModeId) {
     status_t status = checkAccessPermission();
     if (status == OK) {
-        status = mFlinger->setBootDisplayMode(display,
-                                              static_cast<ui::DisplayModeId>(displayModeId));
+        status = mFlinger->setBootDisplayMode(display, DisplayModeId{displayModeId});
     }
     return binderStatusFromStatusT(status);
 }
@@ -7503,30 +7352,6 @@
     return binderStatusFromStatusT(status);
 }
 
-binder::Status SurfaceComposerAIDL::enableVSyncInjections(bool enable) {
-    if (!mFlinger->hasMockHwc()) {
-        return binderStatusFromStatusT(PERMISSION_DENIED);
-    }
-
-    status_t status = checkAccessPermission();
-    if (status == OK) {
-        status = mFlinger->enableVSyncInjections(enable);
-    }
-    return binderStatusFromStatusT(status);
-}
-
-binder::Status SurfaceComposerAIDL::injectVSync(int64_t when) {
-    if (!mFlinger->hasMockHwc()) {
-        return binderStatusFromStatusT(PERMISSION_DENIED);
-    }
-
-    status_t status = checkAccessPermission();
-    if (status == OK) {
-        status = mFlinger->injectVSync(when);
-    }
-    return binderStatusFromStatusT(status);
-}
-
 binder::Status SurfaceComposerAIDL::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
     if (!outLayers) {
         return binderStatusFromStatusT(UNEXPECTED_NULL);
@@ -7846,19 +7671,6 @@
     return binderStatusFromStatusT(status);
 }
 
-binder::Status SurfaceComposerAIDL::addTransactionTraceListener(
-        const sp<gui::ITransactionTraceListener>& listener) {
-    status_t status;
-    IPCThreadState* ipc = IPCThreadState::self();
-    const int uid = ipc->getCallingUid();
-    if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
-        status = mFlinger->addTransactionTraceListener(listener);
-    } else {
-        status = PERMISSION_DENIED;
-    }
-    return binderStatusFromStatusT(status);
-}
-
 binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) {
     *outPriority = mFlinger->getGpuContextPriority();
     return binder::Status::ok();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d005337..c4c5b56 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -29,7 +29,6 @@
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
 #include <ftl/future.h>
-#include <ftl/small_map.h>
 #include <gui/BufferQueue.h>
 #include <gui/CompositorTiming.h>
 #include <gui/FrameTimestamps.h>
@@ -52,32 +51,31 @@
 #include <utils/Trace.h>
 #include <utils/threads.h>
 
-#include <compositionengine/FenceResult.h>
 #include <compositionengine/OutputColorSetting.h>
 #include <scheduler/Fps.h>
 #include <scheduler/PresentLatencyTracker.h>
 #include <scheduler/Time.h>
+#include <ui/FenceResult.h>
 
-#include "ClientCache.h"
+#include "Display/DisplayMap.h"
+#include "Display/PhysicalDisplay.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
 #include "FlagManager.h"
-#include "FrameTracker.h"
 #include "LayerVector.h"
-#include "LocklessQueue.h"
 #include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/RefreshRateStats.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "ThreadContext.h"
-#include "TracedOrdinal.h"
 #include "Tracing/LayerTracing.h"
 #include "Tracing/TransactionTracing.h"
 #include "TransactionCallbackInvoker.h"
+#include "TransactionHandler.h"
 #include "TransactionState.h"
 
 #include <atomic>
@@ -171,13 +169,6 @@
 
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
-struct SurfaceFlingerBE {
-    static const size_t NUM_BUCKETS = 8; // < 1-7, 7+
-    nsecs_t mFrameBuckets[NUM_BUCKETS] = {};
-    nsecs_t mTotalTime = 0;
-    TimePoint mLastPresentTime;
-};
-
 class SurfaceFlinger : public BnSurfaceComposer,
                        public PriorityDumper,
                        private IBinder::DeathRecipient,
@@ -220,10 +211,6 @@
     static uint32_t maxGraphicsWidth;
     static uint32_t maxGraphicsHeight;
 
-    // Indicate if a device has wide color gamut display. This is typically
-    // found on devices with wide color gamut (e.g. Display-P3) display.
-    static bool hasWideColorDisplay;
-
     // Indicate if device wants color management on its display.
     static const constexpr bool useColorManagement = true;
 
@@ -251,9 +238,6 @@
     // starts SurfaceFlinger main loop in the current thread
     void run() ANDROID_API;
 
-    SurfaceFlingerBE& getBE() { return mBE; }
-    const SurfaceFlingerBE& getBE() const { return mBE; }
-
     // Indicates frame activity, i.e. whether commit and/or composite is taking place.
     enum class FrameHint { kNone, kActive };
 
@@ -342,7 +326,6 @@
 
 private:
     friend class BufferLayer;
-    friend class BufferStateLayer;
     friend class Client;
     friend class FpsReporter;
     friend class TunnelModeEnabledReporter;
@@ -351,6 +334,7 @@
     friend class RegionSamplingThread;
     friend class LayerRenderArea;
     friend class LayerTracing;
+    friend class SurfaceComposerAIDL;
 
     // For unit tests
     friend class TestableSurfaceFlinger;
@@ -383,7 +367,20 @@
 
         const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid;
         LayerVector layersSortedByZ;
-        DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
+
+        // TODO(b/241285876): Replace deprecated DefaultKeyedVector with ftl::SmallMap.
+        DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> displays;
+
+        std::optional<size_t> getDisplayIndex(PhysicalDisplayId displayId) const {
+            for (size_t i = 0; i < displays.size(); i++) {
+                const auto& state = displays.valueAt(i);
+                if (state.physical && state.physical->id == displayId) {
+                    return i;
+                }
+            }
+
+            return {};
+        }
 
         bool colorMatrixChanged = true;
         mat4 colorMatrix;
@@ -442,11 +439,6 @@
         FINISHED,
     };
 
-    struct HotplugEvent {
-        hal::HWDisplayId hwcDisplayId;
-        hal::Connection connection = hal::Connection::INVALID;
-    };
-
     template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
     static Dumper dumper(F&& dump) {
         using namespace std::placeholders;
@@ -530,7 +522,7 @@
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&);
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
     status_t getBootDisplayModeSupport(bool* outSupport) const;
-    status_t setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id);
+    status_t setBootDisplayMode(const sp<display::DisplayToken>&, DisplayModeId);
     status_t clearBootDisplayMode(const sp<IBinder>& displayToken);
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
     void setGameContentType(const sp<IBinder>& displayToken, bool on);
@@ -538,8 +530,6 @@
     status_t overrideHdrTypes(const sp<IBinder>& displayToken,
                               const std::vector<ui::Hdr>& hdrTypes);
     status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success);
-    status_t enableVSyncInjections(bool enable);
-    status_t injectVSync(nsecs_t when);
     status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
     status_t getColorManagement(bool* outGetColorManagement) const;
     status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
@@ -596,8 +586,6 @@
 
     status_t setOverrideFrameRate(uid_t uid, float frameRate);
 
-    status_t addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener);
-
     int getGpuContextPriority();
 
     status_t getMaxAcquiredBufferCount(int* buffers) const;
@@ -610,7 +598,7 @@
     void binderDied(const wp<IBinder>& who) override;
 
     // HWC2::ComposerCallback overrides:
-    void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+    void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                             std::optional<hal::VsyncPeriodNanos>) override;
     void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override;
     void onComposerHalRefresh(hal::HWDisplayId) override;
@@ -621,6 +609,9 @@
 
     // ICompositor overrides:
 
+    // Configures physical displays, processing hotplug and/or mode setting via the Composer HAL.
+    void configure() override;
+
     // 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(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) override;
@@ -632,18 +623,17 @@
     // Samples the composited frame via RegionSamplingThread.
     void sample() override;
 
-    /*
-     * ISchedulerCallback
-     */
+    // ISchedulerCallback overrides:
 
     // Toggles hardware VSYNC by calling into HWC.
     void setVsyncEnabled(bool) override;
-    // Sets the desired display mode if allowed by policy.
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override;
+    // Sets the desired display mode per display if allowed by policy .
+    void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) 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.
     void triggerOnFrameRateOverridesChanged() override;
+
     // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
     void toggleKernelIdleTimer() REQUIRES(mStateLock);
     // Get the controller and timeout that will help decide how the kernel idle timer will be
@@ -660,40 +650,44 @@
     // Show spinner with refresh rate overlay
     bool mRefreshRateOverlaySpinner = false;
 
-    // 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 setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id);
+    status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
     // Sets the active mode and a new refresh rate in SF.
-    void updateInternalStateWithChangedMode() REQUIRES(mStateLock);
+    void updateInternalStateWithChangedMode() REQUIRES(mStateLock, kMainThreadContext);
     // Calls to setActiveMode on the main thread if there is a pending mode change
     // that needs to be applied.
-    void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock);
+    void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock, kMainThreadContext);
     void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called when active mode is no longer is progress
     void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
-            REQUIRES(mStateLock);
+            REQUIRES(mStateLock, kMainThreadContext);
 
     // 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);
+    // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
+    // display. Falls back to the display's defaultModeId otherwise.
+    std::optional<DisplayModePtr> getPreferredDisplayMode(PhysicalDisplayId,
+                                                          DisplayModeId defaultModeId) const
+            REQUIRES(mStateLock);
 
-    void commitTransactions() EXCLUDES(mStateLock);
-    void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+    status_t setDesiredDisplayModeSpecsInternal(const sp<DisplayDevice>&,
+                                                const scheduler::RefreshRateConfigs::PolicyVariant&)
+            EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+
+    void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+    void commitTransactionsLocked(uint32_t transactionFlags)
+            REQUIRES(mStateLock, kMainThreadContext);
     void doCommitTransactions() REQUIRES(mStateLock);
 
     // Returns whether a new buffer has been latched.
     bool latchBuffers();
 
     void updateLayerGeometry();
+    void updateLayerMetadataSnapshot();
 
     void updateInputFlinger();
     void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -702,7 +696,7 @@
     void commitInputWindowCommands() REQUIRES(mStateLock);
     void updateCursorAsync();
 
-    void initScheduler(const sp<DisplayDevice>& display) REQUIRES(mStateLock);
+    void initScheduler(const sp<const DisplayDevice>&) REQUIRES(mStateLock);
     void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
     void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
 
@@ -723,11 +717,13 @@
     bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
-
-    int flushPendingTransactionQueues(
-            std::vector<TransactionState>& transactions,
-            std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-            bool tryApplyUnsignaled) REQUIRES(mStateLock) REQUIRES(kMainThreadContext);
+    void addTransactionReadyFilters();
+    TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
+            const TransactionHandler::TransactionFlushState& flushState)
+            REQUIRES(kMainThreadContext);
+    TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
+            const TransactionHandler::TransactionFlushState& flushState)
+            REQUIRES(kMainThreadContext);
 
     uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
                                   int64_t desiredPresentTime, bool isAutoTimestamp,
@@ -745,23 +741,9 @@
 
     void commitOffscreenLayers();
 
-    enum class TransactionReadiness {
-        NotReady,
-        NotReadyBarrier,
-        Ready,
-        ReadyUnsignaled,
-    };
-    TransactionReadiness transactionIsReadyToBeApplied(
-            TransactionState&, const FrameTimelineInfo&, bool isAutoTimestamp,
-            TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>&,
-            const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
-                    bufferLayersReadyToPresent,
-            size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock)
-            REQUIRES(kMainThreadContext);
-
     static LatchUnsignaledConfig getLatchUnsignaledConfig();
     bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
-                               size_t totalTXapplied) const;
+                               bool firstTransaction) const;
     bool applyTransactions(std::vector<TransactionState>& transactions, VsyncId)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -772,10 +754,8 @@
     /*
      * Layer management
      */
-    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 createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
+                         gui::CreateSurfaceResult& outResult);
 
     status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
                                     sp<Layer>* outLayer);
@@ -784,10 +764,10 @@
                                sp<Layer>* outLayer);
 
     status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
-                         sp<IBinder>* outHandle, int32_t* outLayerId);
+                         gui::CreateSurfaceResult& outResult);
 
     status_t mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
-                           sp<IBinder>* outHandle, int32_t* outLayerId);
+                           gui::CreateSurfaceResult& outResult);
 
     // called when all clients have released all their references to
     // this layer meaning it is entirely safe to destroy all
@@ -830,8 +810,10 @@
     /*
      * Display and layer stack management
      */
-    // called when starting, or restarting after system_server death
+
+    // Called during boot, and restart after system_server death.
     void initializeDisplays();
+    void onInitializeDisplays() REQUIRES(mStateLock, kMainThreadContext);
 
     sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
             REQUIRES(mStateLock) {
@@ -867,12 +849,12 @@
     }
 
     sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
-        if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+        if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
             return display;
         }
         // The active display is outdated, so fall back to the primary display.
-        mActiveDisplayToken.clear();
-        return getDisplayDeviceLocked(getPrimaryDisplayTokenLocked());
+        mActiveDisplayId = getPrimaryDisplayIdLocked();
+        return getDisplayDeviceLocked(mActiveDisplayId);
     }
 
     sp<const DisplayDevice> getDefaultDisplayDevice() const EXCLUDES(mStateLock) {
@@ -880,6 +862,21 @@
         return getDefaultDisplayDeviceLocked();
     }
 
+    using DisplayDeviceAndSnapshot =
+            std::pair<sp<DisplayDevice>, display::PhysicalDisplay::SnapshotRef>;
+
+    // Combinator for ftl::Optional<PhysicalDisplay>::and_then.
+    auto getDisplayDeviceAndSnapshot() REQUIRES(mStateLock) {
+        return [this](const display::PhysicalDisplay& display) REQUIRES(
+                       mStateLock) -> ftl::Optional<DisplayDeviceAndSnapshot> {
+            if (auto device = getDisplayDeviceLocked(display.snapshot().displayId())) {
+                return std::make_pair(std::move(device), display.snapshotRef());
+            }
+
+            return {};
+        };
+    }
+
     // Returns the first display that matches a `bool(const DisplayDevice&)` predicate.
     template <typename Predicate>
     sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) {
@@ -895,8 +892,13 @@
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
 
-    bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
-        return display->getDisplayToken() == mActiveDisplayToken;
+    ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack)
+            REQUIRES(mStateLock) {
+        return {layerStack,
+                PhysicalDisplayId::tryCast(displayId)
+                        .and_then(display::getPhysicalDisplay(mPhysicalDisplays))
+                        .transform(&display::PhysicalDisplay::isInternal)
+                        .value_or(false)};
     }
 
     /*
@@ -921,18 +923,30 @@
     std::pair<DisplayModes, DisplayModePtr> loadDisplayModes(PhysicalDisplayId) const
             REQUIRES(mStateLock);
 
+    // TODO(b/241285876): Move to DisplayConfigurator.
+    //
+    // Returns whether displays have been added/changed/removed, i.e. whether ICompositor should
+    // commit display transactions.
+    bool configureLocked() REQUIRES(mStateLock) REQUIRES(kMainThreadContext)
+            EXCLUDES(mHotplugMutex);
+
+    // Returns a string describing the hotplug, or nullptr if it was rejected.
+    const char* processHotplug(PhysicalDisplayId, hal::HWDisplayId, bool connected,
+                               DisplayIdentificationInfo&&) REQUIRES(mStateLock)
+            REQUIRES(kMainThreadContext);
+
     sp<DisplayDevice> setupNewDisplayDeviceInternal(
             const wp<IBinder>& displayToken,
             std::shared_ptr<compositionengine::Display> compositionDisplay,
             const DisplayDeviceState& state,
             const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
-    void processDisplayChangesLocked() REQUIRES(mStateLock);
+    void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext);
     void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
-                               const DisplayDeviceState& drawingState) REQUIRES(mStateLock);
-    void processDisplayHotplugEventsLocked() REQUIRES(mStateLock);
+                               const DisplayDeviceState& drawingState)
+            REQUIRES(mStateLock, kMainThreadContext);
 
     void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
 
@@ -961,21 +975,16 @@
     /*
      * Display identification
      */
-    sp<IBinder> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
+    sp<display::DisplayToken> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
             REQUIRES(mStateLock) {
-        const sp<IBinder> nullToken;
-        return mPhysicalDisplayTokens.get(displayId).value_or(std::cref(nullToken));
+        const sp<display::DisplayToken> nullToken;
+        return mPhysicalDisplays.get(displayId)
+                .transform([](const display::PhysicalDisplay& display) { return display.token(); })
+                .value_or(std::cref(nullToken));
     }
 
     std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
-            const sp<IBinder>& displayToken) const REQUIRES(mStateLock) {
-        for (const auto& [id, token] : mPhysicalDisplayTokens) {
-            if (token == displayToken) {
-                return id;
-            }
-        }
-        return {};
-    }
+            const sp<display::DisplayToken>&) const REQUIRES(mStateLock);
 
     // Returns the first display connected at boot.
     //
@@ -998,14 +1007,17 @@
     VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
-    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock);
+    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay)
+            REQUIRES(mStateLock, kMainThreadContext);
 
-    void onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay);
+    void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&);
 
     /*
      * Debugging & dumpsys
      */
-    void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+    void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
+                       std::string& result) const REQUIRES(mStateLock);
+    void dumpHwcLayersMinidumpLocked(std::string& result) const REQUIRES(mStateLock);
 
     void appendSfConfigString(std::string& result) const;
     void listLayersLocked(std::string& result) const;
@@ -1016,7 +1028,6 @@
     void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext);
 
     void dumpVSync(std::string& result) const REQUIRES(mStateLock);
-    void dumpStaticScreenStats(std::string& result) const;
 
     void dumpCompositionDisplays(std::string& result) const REQUIRES(mStateLock);
     void dumpDisplays(std::string& result) const REQUIRES(mStateLock);
@@ -1053,26 +1064,18 @@
     status_t CheckTransactCodeCredentials(uint32_t code);
 
     // Add transaction to the Transaction Queue
-    void queueTransaction(TransactionState& state);
-    void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
-    void signalSynchronousTransactions(const uint32_t flag);
 
     /*
      * Generic Layer Metadata
      */
     const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
 
-    /*
-     * Misc
-     */
-    std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock);
-
     static int calculateMaxAcquiredBufferCount(Fps refreshRate,
                                                std::chrono::nanoseconds presentLatency);
     int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
 
     void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
-            REQUIRES(mStateLock);
+            REQUIRES(mStateLock, kMainThreadContext);
 
     bool isHdrLayer(Layer* layer) const;
 
@@ -1088,7 +1091,6 @@
     mutable Mutex mStateLock;
     State mCurrentState{LayerVector::StateSet::Current};
     std::atomic<int32_t> mTransactionFlags = 0;
-    std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
     std::atomic<uint32_t> mUniqueTransactionId = 1;
     SortedVector<sp<Layer>> mLayersPendingRemoval;
 
@@ -1136,6 +1138,9 @@
     bool mSomeDataspaceChanged = false;
     bool mForceTransactionDisplayChange = false;
 
+    // Set if LayerMetadata has changed since the last LayerMetadata snapshot.
+    bool mLayerMetadataSnapshotNeeded = false;
+
     // Tracks layers that have pending frames which are candidates for being
     // latched.
     std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
@@ -1155,17 +1160,24 @@
 
     BootStage mBootStage = BootStage::BOOTLOADER;
 
-    std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock);
+    struct HotplugEvent {
+        hal::HWDisplayId hwcDisplayId;
+        hal::Connection connection = hal::Connection::INVALID;
+    };
+
+    std::mutex mHotplugMutex;
+    std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex);
 
     // Displays are composited in `mDisplays` order. Internal displays are inserted at boot and
     // never removed, so take precedence over external and virtual displays.
     //
-    // The static capacities were chosen to exceed a typical number of physical/virtual displays.
-    //
     // May be read from any thread, but must only be written from the main thread.
-    ftl::SmallMap<wp<IBinder>, const sp<DisplayDevice>, 5> mDisplays GUARDED_BY(mStateLock);
-    ftl::SmallMap<PhysicalDisplayId, const sp<IBinder>, 3> mPhysicalDisplayTokens
-            GUARDED_BY(mStateLock);
+    display::DisplayMap<wp<IBinder>, const sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
+
+    display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
+
+    // The inner or outer display for foldables, assuming they have mutually exclusive power states.
+    PhysicalDisplayId mActiveDisplayId GUARDED_BY(mStateLock);
 
     struct {
         DisplayIdGenerator<GpuVirtualDisplayId> gpu;
@@ -1180,7 +1192,6 @@
 
     bool mLayerCachingEnabled = false;
     bool mPropagateBackpressureClientComposition = false;
-    sp<SurfaceInterceptor> mInterceptor;
 
     LayerTracing mLayerTracing{*this};
     bool mLayerTracingEnabled = false;
@@ -1211,17 +1222,6 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
-    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
-            mPendingTransactionQueues;
-    LocklessQueue<TransactionState> mLocklessTransactionQueue;
-    std::atomic<size_t> mPendingTransactionCount = 0;
-    /*
-     * Feature prototyping
-     */
-
-    // Static screen stats
-    bool mHasPoweredOff = false;
-
     std::atomic<size_t> mNumLayers = 0;
 
     // to linkToDeath
@@ -1244,12 +1244,15 @@
     // This property can be used to force SurfaceFlinger to always pick a certain color mode.
     ui::ColorMode mForceColorMode = ui::ColorMode::NATIVE;
 
+    // Whether to enable wide color gamut (e.g. Display P3) for internal displays that support it.
+    // If false, wide color modes are filtered out for all internal displays.
+    bool mSupportsWideColor = false;
+
     ui::Dataspace mDefaultCompositionDataspace;
     ui::Dataspace mWideColorGamutCompositionDataspace;
     ui::Dataspace mColorSpaceAgnosticDataspace;
     float mDimmingRatio = -1.f;
 
-    SurfaceFlingerBE mBE;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
     // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
     // any mutex.
@@ -1257,8 +1260,6 @@
 
     const std::string mHwcServiceName;
 
-    bool hasMockHwc() const { return mHwcServiceName == "mock"; }
-
     /*
      * Scheduler
      */
@@ -1305,7 +1306,7 @@
 
     std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor;
 
-    void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock);
+    void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
 
     // Flag used to set override desired display mode from backdoor
     bool mDebugDisplayModeSetByBackdoor = false;
@@ -1360,8 +1361,6 @@
                 [](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
     }
 
-    wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock);
-
     const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
 
     FlagManager mFlagManager;
@@ -1378,11 +1377,7 @@
         bool early = false;
     } mPowerHintSessionMode;
 
-    void startPowerHintSession() const;
-
-    nsecs_t mAnimationTransactionTimeout = s2ns(5);
-
-    friend class SurfaceComposerAIDL;
+    TransactionHandler mTransactionHandler;
 };
 
 class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1434,8 +1429,6 @@
     binder::Status overrideHdrTypes(const sp<IBinder>& display,
                                     const std::vector<int32_t>& hdrTypes) override;
     binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override;
-    binder::Status enableVSyncInjections(bool enable) override;
-    binder::Status injectVSync(int64_t when) override;
     binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) override;
     binder::Status getColorManagement(bool* outGetColorManagement) override;
     binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override;
@@ -1485,8 +1478,6 @@
             const sp<IBinder>& displayToken,
             std::optional<gui::DisplayDecorationSupport>* outSupport) override;
     binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
-    binder::Status addTransactionTraceListener(
-            const sp<gui::ITransactionTraceListener>& listener) override;
     binder::Status getGpuContextPriority(int32_t* outPriority) override;
     binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
     binder::Status addWindowInfosListener(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 5c96f35..2f1f263 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -22,18 +22,16 @@
 #include <cutils/properties.h>
 #include <ui/GraphicBuffer.h>
 
-#include "BufferStateLayer.h"
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
 #include "NativeWindowSurface.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerDefaultFactory.h"
 #include "SurfaceFlingerProperties.h"
-#include "SurfaceInterceptor.h"
 
 #include "DisplayHardware/ComposerHal.h"
+#include "FrameTimeline/FrameTimeline.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
 #include "Scheduler/VsyncController.h"
@@ -55,10 +53,6 @@
     }
 }
 
-sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
-    return sp<android::impl::SurfaceInterceptor>::make();
-}
-
 sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
         bool timestampPropertyValue) {
     return sp<StartPropertySetThread>::make(timestampPropertyValue);
@@ -89,12 +83,12 @@
     return compositionengine::impl::createCompositionEngine();
 }
 
-sp<BufferStateLayer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
-    return sp<BufferStateLayer>::make(args);
+sp<Layer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
+    return sp<Layer>::make(args);
 }
 
-sp<EffectLayer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
-    return sp<EffectLayer>::make(args);
+sp<Layer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
+    return sp<Layer>::make(args);
 }
 
 std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() {
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 8d00379..447a02f 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,6 @@
     std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) override;
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override;
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
@@ -41,8 +40,8 @@
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>&) override;
     std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override;
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) override;
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) override;
+    sp<Layer> createEffectLayer(const LayerCreationArgs& args) override;
     std::unique_ptr<FrameTracer> createFrameTracer() override;
     std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
             std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 291838f..9c4d5c8 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -31,9 +31,7 @@
 typedef int32_t PixelFormat;
 
 class BufferLayerConsumer;
-class BufferStateLayer;
 class DisplayDevice;
-class EffectLayer;
 class FrameTracer;
 class GraphicBuffer;
 class HWComposer;
@@ -42,7 +40,6 @@
 class Layer;
 class StartPropertySetThread;
 class SurfaceFlinger;
-class SurfaceInterceptor;
 class TimeStats;
 
 struct DisplayDeviceCreationArgs;
@@ -73,7 +70,6 @@
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
     virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) = 0;
-    virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
             bool timestampPropertyValue) = 0;
@@ -90,8 +86,8 @@
 
     virtual std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() = 0;
 
-    virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
-    virtual sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<Layer> createEffectLayer(const LayerCreationArgs& args) = 0;
     virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
     virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
             std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
deleted file mode 100644
index 66691c2..0000000
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ /dev/null
@@ -1,720 +0,0 @@
-/*
- * 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 "SurfaceInterceptor"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "Layer.h"
-#include "SurfaceFlinger.h"
-#include "SurfaceInterceptor.h"
-
-#include <fstream>
-
-#include <android-base/file.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-// TODO(marissaw): add new layer state values to SurfaceInterceptor
-
-SurfaceInterceptor::~SurfaceInterceptor() = default;
-
-namespace impl {
-
-void SurfaceInterceptor::addTransactionTraceListener(
-        const sp<gui::ITransactionTraceListener>& listener) {
-    sp<IBinder> asBinder = IInterface::asBinder(listener);
-
-    std::scoped_lock lock(mListenersMutex);
-
-    asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
-
-    listener->onToggled(mEnabled); // notifies of current state
-
-    mTraceToggledListeners.emplace(asBinder, listener);
-}
-
-void SurfaceInterceptor::binderDied(const wp<IBinder>& who) {
-    std::scoped_lock lock(mListenersMutex);
-    mTraceToggledListeners.erase(who);
-}
-
-void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
-        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
-{
-    if (mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mTraceToggledListeners) {
-            listener->onToggled(true);
-        }
-    }
-    mEnabled = true;
-    std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
-    saveExistingDisplaysLocked(displays);
-    saveExistingSurfacesLocked(layers);
-}
-
-void SurfaceInterceptor::disable() {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mTraceToggledListeners) {
-            listener->onToggled(false);
-        }
-    }
-    mEnabled = false;
-    std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
-    status_t err(writeProtoFileLocked());
-    ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
-    ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
-    mTrace.Clear();
-}
-
-bool SurfaceInterceptor::isEnabled() {
-    return mEnabled;
-}
-
-void SurfaceInterceptor::saveExistingDisplaysLocked(
-        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
-{
-    // Caveat: The initial snapshot does not capture the power mode of the existing displays
-    ATRACE_CALL();
-    for (size_t i = 0 ; i < displays.size() ; i++) {
-        addDisplayCreationLocked(createTraceIncrementLocked(), displays[i]);
-        addInitialDisplayStateLocked(createTraceIncrementLocked(), displays[i]);
-    }
-}
-
-void SurfaceInterceptor::saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers) {
-    ATRACE_CALL();
-    for (const auto& l : layers) {
-        l->traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* layer) {
-            addSurfaceCreationLocked(createTraceIncrementLocked(), sp<Layer>::fromExisting(layer));
-            addInitialSurfaceStateLocked(createTraceIncrementLocked(),
-                                         sp<Layer>::fromExisting(layer));
-        });
-    }
-}
-
-void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    Transaction* transaction(increment->mutable_transaction());
-    const uint32_t layerFlags = layer->getTransactionFlags();
-    transaction->set_synchronous(layerFlags & BnSurfaceComposer::eSynchronous);
-    transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
-
-    const int32_t layerId(getLayerId(layer));
-    addPositionLocked(transaction, layerId, layer->mDrawingState.transform.tx(),
-                      layer->mDrawingState.transform.ty());
-    addDepthLocked(transaction, layerId, layer->mDrawingState.z);
-    addAlphaLocked(transaction, layerId, layer->mDrawingState.color.a);
-    addTransparentRegionLocked(transaction, layerId,
-                               layer->mDrawingState.activeTransparentRegion_legacy);
-    addLayerStackLocked(transaction, layerId, layer->mDrawingState.layerStack);
-    addCropLocked(transaction, layerId, layer->mDrawingState.crop);
-    addCornerRadiusLocked(transaction, layerId, layer->mDrawingState.cornerRadius);
-    addBackgroundBlurRadiusLocked(transaction, layerId, layer->mDrawingState.backgroundBlurRadius);
-    addBlurRegionsLocked(transaction, layerId, layer->mDrawingState.blurRegions);
-    addFlagsLocked(transaction, layerId, layer->mDrawingState.flags,
-                   layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
-                           layer_state_t::eLayerSecure);
-    addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mDrawingParent));
-    addRelativeParentLocked(transaction, layerId,
-                            getLayerIdFromWeakRef(layer->mDrawingState.zOrderRelativeOf),
-                            layer->mDrawingState.z);
-    addShadowRadiusLocked(transaction, layerId, layer->mDrawingState.shadowRadius);
-    addTrustedOverlayLocked(transaction, layerId, layer->mDrawingState.isTrustedOverlay);
-}
-
-void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
-        const DisplayDeviceState& display)
-{
-    Transaction* transaction(increment->mutable_transaction());
-    transaction->set_synchronous(false);
-    transaction->set_animation(false);
-
-    addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface);
-    addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
-    addDisplayFlagsLocked(transaction, display.sequenceId, display.flags);
-    addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
-    addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
-                               display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
-}
-
-status_t SurfaceInterceptor::writeProtoFileLocked() {
-    ATRACE_CALL();
-    std::string output;
-
-    if (!mTrace.IsInitialized()) {
-        return NOT_ENOUGH_DATA;
-    }
-    if (!mTrace.SerializeToString(&output)) {
-        return PERMISSION_DENIED;
-    }
-    if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
-        return PERMISSION_DENIED;
-    }
-
-    return NO_ERROR;
-}
-
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const {
-    sp<IBinder> handle = weakHandle.promote();
-    return Layer::fromHandle(handle).promote();
-}
-
-int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
-    return layer->sequence;
-}
-
-int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) const {
-    if (layer == nullptr) {
-        return -1;
-    }
-    auto strongLayer = layer.promote();
-    return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
-}
-
-int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const {
-    if (handle == nullptr) {
-        return -1;
-    }
-    const sp<const Layer> layer = Layer::fromHandle(handle).promote();
-    return layer == nullptr ? -1 : getLayerId(layer);
-}
-
-Increment* SurfaceInterceptor::createTraceIncrementLocked() {
-    Increment* increment(mTrace.add_increment());
-    increment->set_time_stamp(elapsedRealtimeNano());
-    return increment;
-}
-
-SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transaction,
-        int32_t layerId)
-{
-    SurfaceChange* change(transaction->add_surface_change());
-    change->set_id(layerId);
-    return change;
-}
-
-DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
-        int32_t sequenceId)
-{
-    DisplayChange* dispChange(transaction->add_display_change());
-    dispChange->set_id(sequenceId);
-    return dispChange;
-}
-
-void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) {
-    protoRect->set_left(rect.left);
-    protoRect->set_top(rect.top);
-    protoRect->set_right(rect.right);
-    protoRect->set_bottom(rect.bottom);
-}
-
-void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid,
-                                                    int32_t uid) {
-    Origin* origin(transaction->mutable_origin());
-    origin->set_pid(pid);
-    origin->set_uid(uid);
-}
-
-void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
-        float x, float y)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    PositionChange* posChange(change->mutable_position());
-    posChange->set_x(x);
-    posChange->set_y(y);
-}
-
-void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId,
-        uint32_t z)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    LayerChange* depthChange(change->mutable_layer());
-    depthChange->set_layer(z);
-}
-
-void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w,
-        uint32_t h)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    SizeChange* sizeChange(change->mutable_size());
-    sizeChange->set_w(w);
-    sizeChange->set_h(h);
-}
-
-void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId,
-        float alpha)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    AlphaChange* alphaChange(change->mutable_alpha());
-    alphaChange->set_alpha(alpha);
-}
-
-void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId,
-        const layer_state_t::matrix22_t& matrix)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    MatrixChange* matrixChange(change->mutable_matrix());
-    matrixChange->set_dsdx(matrix.dsdx);
-    matrixChange->set_dtdx(matrix.dtdx);
-    matrixChange->set_dsdy(matrix.dsdy);
-    matrixChange->set_dtdy(matrix.dtdy);
-}
-
-void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction,
-        int32_t layerId, const Region& transRegion)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint());
-
-    for (const auto& rect : transRegion) {
-        Rectangle* protoRect(transparentChange->add_region());
-        setProtoRectLocked(protoRect, rect);
-    }
-}
-
-void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags,
-                                        uint8_t mask) {
-    // There can be multiple flags changed
-    if (mask & layer_state_t::eLayerHidden) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        HiddenFlagChange* flagChange(change->mutable_hidden_flag());
-        flagChange->set_hidden_flag(flags & layer_state_t::eLayerHidden);
-    }
-    if (mask & layer_state_t::eLayerOpaque) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
-        flagChange->set_opaque_flag(flags & layer_state_t::eLayerOpaque);
-    }
-    if (mask & layer_state_t::eLayerSecure) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        SecureFlagChange* flagChange(change->mutable_secure_flag());
-        flagChange->set_secure_flag(flags & layer_state_t::eLayerSecure);
-    }
-}
-
-void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
-                                             ui::LayerStack layerStack) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    LayerStackChange* layerStackChange(change->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack.id);
-}
-
-void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
-        const Rect& rect)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    CropChange* cropChange(change->mutable_crop());
-    Rectangle* protoRect(cropChange->mutable_rectangle());
-    setProtoRectLocked(protoRect, rect);
-}
-
-void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId,
-                                       float cornerRadius)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius());
-    cornerRadiusChange->set_corner_radius(cornerRadius);
-}
-
-void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
-                                                       int32_t backgroundBlurRadius) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius());
-    blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
-}
-
-void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
-                                              const std::vector<BlurRegion>& blurRegions) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions());
-    for (const auto blurRegion : blurRegions) {
-        const auto blurRegionChange = blurRegionsChange->add_blur_regions();
-        blurRegionChange->set_blur_radius(blurRegion.blurRadius);
-        blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL);
-        blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR);
-        blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL);
-        blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR);
-        blurRegionChange->set_alpha(blurRegion.alpha);
-        blurRegionChange->set_left(blurRegion.left);
-        blurRegionChange->set_top(blurRegion.top);
-        blurRegionChange->set_right(blurRegion.right);
-        blurRegionChange->set_bottom(blurRegion.bottom);
-    }
-}
-
-void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
-                                           int32_t parentId) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    ReparentChange* overrideChange(change->mutable_reparent());
-    overrideChange->set_parent_id(parentId);
-}
-
-void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
-                                                 int32_t parentId, int z) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    RelativeParentChange* overrideChange(change->mutable_relative_parent());
-    overrideChange->set_relative_parent_id(parentId);
-    overrideChange->set_z(z);
-}
-
-void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId,
-                                               float shadowRadius) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    ShadowRadiusChange* overrideChange(change->mutable_shadow_radius());
-    overrideChange->set_radius(shadowRadius);
-}
-
-void SurfaceInterceptor::addTrustedOverlayLocked(Transaction* transaction, int32_t layerId,
-                                                 bool isTrustedOverlay) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    TrustedOverlayChange* overrideChange(change->mutable_trusted_overlay());
-    overrideChange->set_is_trusted_overlay(isTrustedOverlay);
-}
-
-void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
-        const layer_state_t& state)
-{
-    const sp<const Layer> layer(getLayer(state.surface));
-    if (layer == nullptr) {
-        ALOGE("An existing layer could not be retrieved with the surface "
-                "from the layer_state_t surface in the update transaction");
-        return;
-    }
-
-    const int32_t layerId(getLayerId(layer));
-
-    if (state.what & layer_state_t::ePositionChanged) {
-        addPositionLocked(transaction, layerId, state.x, state.y);
-    }
-    if (state.what & layer_state_t::eLayerChanged) {
-        addDepthLocked(transaction, layerId, state.z);
-    }
-    if (state.what & layer_state_t::eSizeChanged) {
-        addSizeLocked(transaction, layerId, state.w, state.h);
-    }
-    if (state.what & layer_state_t::eAlphaChanged) {
-        addAlphaLocked(transaction, layerId, state.alpha);
-    }
-    if (state.what & layer_state_t::eMatrixChanged) {
-        addMatrixLocked(transaction, layerId, state.matrix);
-    }
-    if (state.what & layer_state_t::eTransparentRegionChanged) {
-        addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
-    }
-    if (state.what & layer_state_t::eFlagsChanged) {
-        addFlagsLocked(transaction, layerId, state.flags, state.mask);
-    }
-    if (state.what & layer_state_t::eLayerStackChanged) {
-        addLayerStackLocked(transaction, layerId, state.layerStack);
-    }
-    if (state.what & layer_state_t::eCropChanged) {
-        addCropLocked(transaction, layerId, state.crop);
-    }
-    if (state.what & layer_state_t::eCornerRadiusChanged) {
-        addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
-    }
-    if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
-        addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
-    }
-    if (state.what & layer_state_t::eBlurRegionsChanged) {
-        addBlurRegionsLocked(transaction, layerId, state.blurRegions);
-    }
-    if (state.what & layer_state_t::eReparent) {
-        auto parentHandle = (state.parentSurfaceControlForChild)
-                ? state.parentSurfaceControlForChild->getHandle()
-                : nullptr;
-        addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
-    }
-    if (state.what & layer_state_t::eRelativeLayerChanged) {
-        addRelativeParentLocked(transaction, layerId,
-                                getLayerIdFromHandle(
-                                        state.relativeLayerSurfaceControl->getHandle()),
-                                state.z);
-    }
-    if (state.what & layer_state_t::eShadowRadiusChanged) {
-        addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
-    }
-    if (state.what & layer_state_t::eTrustedOverlayChanged) {
-        addTrustedOverlayLocked(transaction, layerId, state.isTrustedOverlay);
-    }
-    if (state.what & layer_state_t::eStretchChanged) {
-        ALOGW("SurfaceInterceptor not implemented for eStretchChanged");
-    }
-}
-
-void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
-        const DisplayState& state, int32_t sequenceId)
-{
-    if (state.what & DisplayState::eSurfaceChanged) {
-        addDisplaySurfaceLocked(transaction, sequenceId, state.surface);
-    }
-    if (state.what & DisplayState::eLayerStackChanged) {
-        addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack);
-    }
-    if (state.what & DisplayState::eFlagsChanged) {
-        addDisplayFlagsLocked(transaction, sequenceId, state.flags);
-    }
-    if (state.what & DisplayState::eDisplaySizeChanged) {
-        addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
-    }
-    if (state.what & DisplayState::eDisplayProjectionChanged) {
-        addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
-                                   state.layerStackSpaceRect, state.orientedDisplaySpaceRect);
-    }
-}
-
-void SurfaceInterceptor::addTransactionLocked(
-        Increment* increment, const Vector<ComposerState>& stateUpdates,
-        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-        const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPid,
-        int originUid, uint64_t transactionId) {
-    Transaction* transaction(increment->mutable_transaction());
-    transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
-    transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
-    setTransactionOriginLocked(transaction, originPid, originUid);
-    transaction->set_id(transactionId);
-    for (const auto& compState: stateUpdates) {
-        addSurfaceChangesLocked(transaction, compState.state);
-    }
-    for (const auto& disp: changedDisplays) {
-        ssize_t dpyIdx = displays.indexOfKey(disp.token);
-        if (dpyIdx >= 0) {
-            const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
-            addDisplayChangesLocked(transaction, disp, dispState.sequenceId);
-        }
-    }
-}
-
-void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    SurfaceCreation* creation(increment->mutable_surface_creation());
-    creation->set_id(getLayerId(layer));
-    creation->set_name(layer->getName());
-    creation->set_w(layer->mDrawingState.active_legacy.w);
-    creation->set_h(layer->mDrawingState.active_legacy.h);
-}
-
-void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    SurfaceDeletion* deletion(increment->mutable_surface_deletion());
-    deletion->set_id(getLayerId(layer));
-}
-
-void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, int32_t layerId,
-        uint32_t width, uint32_t height, uint64_t frameNumber)
-{
-    BufferUpdate* update(increment->mutable_buffer_update());
-    update->set_id(layerId);
-    update->set_w(width);
-    update->set_h(height);
-    update->set_frame_number(frameNumber);
-}
-
-void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp) {
-    VSyncEvent* event(increment->mutable_vsync_event());
-    event->set_when(timestamp);
-}
-
-void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
-        const sp<const IGraphicBufferProducer>& surface)
-{
-    if (surface == nullptr) {
-        return;
-    }
-    uint64_t bufferQueueId = 0;
-    status_t err(surface->getUniqueId(&bufferQueueId));
-    if (err == NO_ERROR) {
-        DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-        DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
-        surfaceChange->set_buffer_queue_id(bufferQueueId);
-        surfaceChange->set_buffer_queue_name(surface->getConsumerName().string());
-    }
-    else {
-        ALOGE("invalid graphic buffer producer received while tracing a display change (%s)",
-                strerror(-err));
-    }
-}
-
-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.id);
-}
-
-void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId,
-                                               uint32_t flags) {
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    DisplayFlagsChange* flagsChange(dispChange->mutable_flags());
-    flagsChange->set_flags(flags);
-}
-
-void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId,
-        uint32_t w, uint32_t h)
-{
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    SizeChange* sizeChange(dispChange->mutable_size());
-    sizeChange->set_w(w);
-    sizeChange->set_h(h);
-}
-
-void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
-        int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame)
-{
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    ProjectionChange* projectionChange(dispChange->mutable_projection());
-    projectionChange->set_orientation(orientation);
-    Rectangle* viewportRect(projectionChange->mutable_viewport());
-    setProtoRectLocked(viewportRect, viewport);
-    Rectangle* frameRect(projectionChange->mutable_frame());
-    setProtoRectLocked(frameRect, frame);
-}
-
-void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment,
-        const DisplayDeviceState& info)
-{
-    DisplayCreation* creation(increment->mutable_display_creation());
-    creation->set_id(info.sequenceId);
-    creation->set_name(info.displayName);
-    creation->set_is_secure(info.isSecure);
-    if (info.physical) {
-        creation->set_display_id(info.physical->id.value);
-    }
-}
-
-void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t sequenceId) {
-    DisplayDeletion* deletion(increment->mutable_display_deletion());
-    deletion->set_id(sequenceId);
-}
-
-void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId,
-        int32_t mode)
-{
-    PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
-    powerModeUpdate->set_id(sequenceId);
-    powerModeUpdate->set_mode(mode);
-}
-
-void SurfaceInterceptor::saveTransaction(
-        const Vector<ComposerState>& stateUpdates,
-        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-        const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, int originUid,
-        uint64_t transactionId) {
-    if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
-                         flags, originPid, originUid, transactionId);
-}
-
-void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
-    if (!mEnabled || layer == nullptr) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
-}
-
-void SurfaceInterceptor::saveSurfaceDeletion(const sp<const Layer>& layer) {
-    if (!mEnabled || layer == nullptr) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
-}
-
-/**
- * Here we pass the layer by ID instead of by sp<> since this is called without
- * holding the state-lock from a Binder thread. If we required the caller
- * to pass 'this' by sp<> the temporary sp<> constructed could end up
- * being the last reference and we might accidentally destroy the Layer
- * from this binder thread.
- */
-void SurfaceInterceptor::saveBufferUpdate(int32_t layerId, uint32_t width,
-        uint32_t height, uint64_t frameNumber)
-{
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addBufferUpdateLocked(createTraceIncrementLocked(), layerId, width, height, frameNumber);
-}
-
-void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
-    if (!mEnabled) {
-        return;
-    }
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addVSyncUpdateLocked(createTraceIncrementLocked(), timestamp);
-}
-
-void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addDisplayCreationLocked(createTraceIncrementLocked(), info);
-}
-
-void SurfaceInterceptor::saveDisplayDeletion(int32_t sequenceId) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addDisplayDeletionLocked(createTraceIncrementLocked(), sequenceId);
-}
-
-void SurfaceInterceptor::savePowerModeUpdate(int32_t sequenceId, int32_t mode) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addPowerModeUpdateLocked(createTraceIncrementLocked(), sequenceId, mode);
-}
-
-} // namespace impl
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
deleted file mode 100644
index 970c3e5..0000000
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_SURFACEINTERCEPTOR_H
-#define ANDROID_SURFACEINTERCEPTOR_H
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <mutex>
-
-#include <binder/IBinder.h>
-
-#include <gui/LayerState.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/SortedVector.h>
-#include <utils/StrongPointer.h>
-#include <utils/Vector.h>
-
-#include "DisplayDevice.h"
-
-namespace android {
-
-class BufferItem;
-class Layer;
-class SurfaceFlinger;
-struct ComposerState;
-struct DisplayDeviceState;
-struct DisplayState;
-struct layer_state_t;
-using Transaction = surfaceflinger::Transaction;
-using Trace = surfaceflinger::Trace;
-using Rectangle = surfaceflinger::Rectangle;
-using SurfaceChange = surfaceflinger::SurfaceChange;
-using Increment = surfaceflinger::Increment;
-using DisplayChange = surfaceflinger::DisplayChange;
-
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
-
-class SurfaceInterceptor : public IBinder::DeathRecipient {
-public:
-    virtual ~SurfaceInterceptor();
-
-    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
-    virtual void enable(const SortedVector<sp<Layer>>& layers,
-                        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) = 0;
-    virtual void disable() = 0;
-    virtual bool isEnabled() = 0;
-
-    virtual void addTransactionTraceListener(
-            const sp<gui::ITransactionTraceListener>& listener) = 0;
-    virtual void binderDied(const wp<IBinder>& who) = 0;
-
-    // Intercept display and surface transactions
-    virtual void saveTransaction(
-            const Vector<ComposerState>& stateUpdates,
-            const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-            const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
-            int originUid, uint64_t transactionId) = 0;
-
-    // Intercept surface data
-    virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
-    virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0;
-    virtual void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
-                                  uint64_t frameNumber) = 0;
-
-    // Intercept display data
-    virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0;
-    virtual void saveDisplayDeletion(int32_t sequenceId) = 0;
-    virtual void savePowerModeUpdate(int32_t sequenceId, int32_t mode) = 0;
-    virtual void saveVSyncEvent(nsecs_t timestamp) = 0;
-};
-
-namespace impl {
-
-/*
- * SurfaceInterceptor intercepts and stores incoming streams of window
- * properties on SurfaceFlinger.
- */
-class SurfaceInterceptor final : public android::SurfaceInterceptor {
-public:
-    SurfaceInterceptor() = default;
-    ~SurfaceInterceptor() override = default;
-
-    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
-    void enable(const SortedVector<sp<Layer>>& layers,
-                const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) override;
-    void disable() override;
-    bool isEnabled() override;
-
-    void addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener) override;
-    void binderDied(const wp<IBinder>& who) override;
-
-    // Intercept display and surface transactions
-    void saveTransaction(const Vector<ComposerState>& stateUpdates,
-                         const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-                         const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
-                         int originUid, uint64_t transactionId) override;
-
-    // Intercept surface data
-    void saveSurfaceCreation(const sp<const Layer>& layer) override;
-    void saveSurfaceDeletion(const sp<const Layer>& layer) override;
-    void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
-                          uint64_t frameNumber) override;
-
-    // Intercept display data
-    void saveDisplayCreation(const DisplayDeviceState& info) override;
-    void saveDisplayDeletion(int32_t sequenceId) override;
-    void savePowerModeUpdate(int32_t sequenceId, int32_t mode) override;
-    void saveVSyncEvent(nsecs_t timestamp) override;
-
-private:
-    // The creation increments of Surfaces and Displays do not contain enough information to capture
-    // the initial state of each object, so a transaction with all of the missing properties is
-    // performed at the initial snapshot for each display and surface.
-    void saveExistingDisplaysLocked(
-            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
-    void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers);
-    void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer);
-    void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
-
-    status_t writeProtoFileLocked();
-    const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const;
-    int32_t getLayerId(const sp<const Layer>& layer) const;
-    int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
-    int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const;
-
-    Increment* createTraceIncrementLocked();
-    void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
-    void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
-    void addBufferUpdateLocked(Increment* increment, int32_t layerId, uint32_t width,
-            uint32_t height, uint64_t frameNumber);
-    void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
-    void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
-    void addDisplayDeletionLocked(Increment* increment, int32_t sequenceId);
-    void addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode);
-
-    // Add surface transactions to the trace
-    SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
-    void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
-    void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
-    void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
-    void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
-    void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
-    void addMatrixLocked(Transaction* transaction, int32_t layerId,
-            const layer_state_t::matrix22_t& matrix);
-    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, 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,
-                                       int32_t backgroundBlurRadius);
-    void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
-                              const std::vector<BlurRegion>& effectRegions);
-    void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
-    void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
-                              const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-                              const Vector<DisplayState>& changedDisplays,
-                              uint32_t transactionFlags, int originPid, int originUid,
-                              uint64_t transactionId);
-    void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
-    void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
-                                 int z);
-    void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
-    void addTrustedOverlayLocked(Transaction* transaction, int32_t layerId, bool isTrustedOverlay);
-
-    // Add display transactions to the trace
-    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, 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);
-    void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId,
-            int32_t orientation, const Rect& viewport, const Rect& frame);
-    void addDisplayChangesLocked(Transaction* transaction,
-            const DisplayState& state, int32_t sequenceId);
-
-    // Add transaction origin to trace
-    void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid);
-
-    bool mEnabled {false};
-    std::string mOutputFileName {DEFAULT_FILENAME};
-    std::mutex mTraceMutex {};
-    Trace mTrace {};
-    std::mutex mListenersMutex;
-    std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners
-            GUARDED_BY(mListenersMutex);
-};
-
-} // namespace impl
-
-} // namespace android
-
-#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 77dec6f..3418c82 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -88,10 +88,7 @@
     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);
     }
@@ -114,7 +111,7 @@
     }
 
     if (layer.what & layer_state_t::eAlphaChanged) {
-        proto.set_alpha(layer.alpha);
+        proto.set_alpha(layer.color.a);
     }
 
     if (layer.what & layer_state_t::eColorChanged) {
@@ -126,8 +123,8 @@
     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::eBufferTransformChanged) {
+        proto.set_transform(layer.bufferTransform);
     }
     if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) {
         proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
@@ -376,10 +373,6 @@
     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();
     }
@@ -402,7 +395,7 @@
     }
 
     if (proto.what() & layer_state_t::eAlphaChanged) {
-        layer.alpha = proto.alpha();
+        layer.color.a = proto.alpha();
     }
 
     if (proto.what() & layer_state_t::eColorChanged) {
@@ -414,8 +407,8 @@
     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::eBufferTransformChanged) {
+        layer.bufferTransform = proto.transform();
     }
     if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) {
         layer.transformToDisplayInverse = proto.transform_to_display_inverse();
@@ -459,7 +452,7 @@
             layer.parentSurfaceControlForChild =
                     sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
                                              mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
-                                             static_cast<int32_t>(layerId));
+                                             static_cast<int32_t>(layerId), "");
         }
     }
     if (proto.what() & layer_state_t::eRelativeLayerChanged) {
@@ -470,7 +463,7 @@
             layer.relativeLayerSurfaceControl =
                     sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
                                              mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
-                                             static_cast<int32_t>(layerId));
+                                             static_cast<int32_t>(layerId), "");
         }
         layer.z = proto.z();
     }
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 872a901..2232bb9 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include <gui/fake/BufferData.h>
 #include <layerproto/TransactionProto.h>
 #include <utils/RefBase.h>
 
@@ -43,35 +44,6 @@
     TracingLayerCreationArgs args;
 };
 
-// Class which exposes buffer properties from BufferData without holding on to the actual buffer
-// handle.
-class BufferDataStub : public BufferData {
-public:
-    BufferDataStub(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
-                   uint64_t outUsage)
-          : mBufferId(bufferId),
-            mWidth(width),
-            mHeight(height),
-            mPixelFormat(pixelFormat),
-            mOutUsage(outUsage) {}
-    bool hasBuffer() const override { return mBufferId != 0; }
-    bool hasSameBuffer(const BufferData& other) const override {
-        return getId() == other.getId() && frameNumber == other.frameNumber;
-    }
-    uint32_t getWidth() const override { return mWidth; }
-    uint32_t getHeight() const override { return mHeight; }
-    uint64_t getId() const override { return mBufferId; }
-    PixelFormat getPixelFormat() const override { return mPixelFormat; }
-    uint64_t getUsage() const override { return mOutUsage; }
-
-private:
-    uint64_t mBufferId;
-    uint32_t mWidth;
-    uint32_t mHeight;
-    int32_t mPixelFormat;
-    uint64_t mOutUsage;
-};
-
 class TransactionProtoParser {
 public:
     // Utility class to map handles to ids and buffers to buffer properties without pulling
@@ -87,7 +59,7 @@
         virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
                                                            uint32_t height, int32_t pixelFormat,
                                                            uint64_t usage) const {
-            return std::make_shared<BufferDataStub>(bufferId, width, height, pixelFormat, usage);
+            return std::make_shared<fake::BufferData>(bufferId, width, height, pixelFormat, usage);
         }
         virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */,
                                                          uint64_t* /* outBufferId */,
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 312d4ab..d0364f2 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -47,10 +47,6 @@
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return sp<android::impl::SurfaceInterceptor>::make();
-    }
-
     sp<StartPropertySetThread> createStartPropertySetThread(
             bool /* timestampPropertyValue */) override {
         return sp<StartPropertySetThread>();
@@ -80,13 +76,11 @@
         return compositionengine::impl::createCompositionEngine();
     }
 
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) {
-        return sp<BufferStateLayer>::make(args);
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) {
+        return sp<Layer>::make(args);
     }
 
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) {
-        return sp<EffectLayer>::make(args);
-    }
+    sp<Layer> createEffectLayer(const LayerCreationArgs& args) { return sp<Layer>::make(args); }
 
     std::unique_ptr<FrameTracer> createFrameTracer() override {
         return std::make_unique<testing::NiceMock<mock::FrameTracer>>();
@@ -215,8 +209,7 @@
             TracingLayerCreationArgs tracingArgs;
             parser.fromProto(entry.added_layers(j), tracingArgs);
 
-            sp<IBinder> outHandle;
-            int32_t outLayerId;
+            gui::CreateSurfaceResult outResult;
             LayerCreationArgs args(mFlinger.flinger(), nullptr /* client */, tracingArgs.name,
                                    tracingArgs.flags, LayerMetadata());
             args.sequence = std::make_optional<int32_t>(tracingArgs.layerId);
@@ -230,16 +223,15 @@
                 } else if (tracingArgs.parentId != -1) {
                     parentHandle = dataMapper->getLayerHandle(tracingArgs.parentId);
                 }
-                mFlinger.createLayer(args, &outHandle, parentHandle, &outLayerId,
-                                     nullptr /* parentLayer */, nullptr /* outTransformHint */);
+                mFlinger.createLayer(args, parentHandle, outResult);
             } else {
                 sp<IBinder> mirrorFromHandle = dataMapper->getLayerHandle(tracingArgs.mirrorFromId);
-                mFlinger.mirrorLayer(args, mirrorFromHandle, &outHandle, &outLayerId);
+                mFlinger.mirrorLayer(args, mirrorFromHandle, outResult);
             }
-            LOG_ALWAYS_FATAL_IF(outLayerId != tracingArgs.layerId,
+            LOG_ALWAYS_FATAL_IF(outResult.layerId != tracingArgs.layerId,
                                 "Could not create layer expected:%d actual:%d", tracingArgs.layerId,
-                                outLayerId);
-            dataMapper->mLayerHandles[tracingArgs.layerId] = outHandle;
+                                outResult.layerId);
+            dataMapper->mLayerHandles[tracingArgs.layerId] = outResult.handle;
         }
 
         for (int j = 0; j < entry.transactions_size(); j++) {
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 59abe33..23ea7a5 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -26,10 +26,10 @@
 
 #include <android-base/thread_annotations.h>
 #include <binder/IBinder.h>
-#include <compositionengine/FenceResult.h>
 #include <ftl/future.h>
 #include <gui/ITransactionCompletedListener.h>
 #include <ui/Fence.h>
+#include <ui/FenceResult.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/TransactionHandler.cpp b/services/surfaceflinger/TransactionHandler.cpp
new file mode 100644
index 0000000..6c6a487
--- /dev/null
+++ b/services/surfaceflinger/TransactionHandler.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionHandler"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cutils/trace.h>
+#include <utils/Log.h>
+
+#include "TransactionHandler.h"
+
+namespace android {
+
+void TransactionHandler::queueTransaction(TransactionState&& state) {
+    mLocklessTransactionQueue.push(std::move(state));
+    mPendingTransactionCount.fetch_add(1);
+    ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+}
+
+std::vector<TransactionState> TransactionHandler::flushTransactions() {
+    while (!mLocklessTransactionQueue.isEmpty()) {
+        auto maybeTransaction = mLocklessTransactionQueue.pop();
+        if (!maybeTransaction.has_value()) {
+            break;
+        }
+        auto transaction = maybeTransaction.value();
+        mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
+    }
+
+    // Collect transaction that are ready to be applied.
+    std::vector<TransactionState> transactions;
+    TransactionFlushState flushState;
+    flushState.queueProcessTime = systemTime();
+    // Transactions with a buffer pending on a barrier may be on a different applyToken
+    // than the transaction which satisfies our barrier. In fact this is the exact use case
+    // that the primitive is designed for. This means we may first process
+    // the barrier dependent transaction, determine it ineligible to complete
+    // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
+    // The barrier dependent transaction was eligible to be presented in this frame
+    // but we would have prevented it without case. To fix this we continually
+    // loop through flushPendingTransactionQueues until we perform an iteration
+    // where the number of transactionsPendingBarrier doesn't change. This way
+    // we can continue to resolve dependency chains of barriers as far as possible.
+    int lastTransactionsPendingBarrier = 0;
+    int transactionsPendingBarrier = 0;
+    do {
+        lastTransactionsPendingBarrier = transactionsPendingBarrier;
+        // Collect transactions that are ready to be applied.
+        transactionsPendingBarrier = flushPendingTransactionQueues(transactions, flushState);
+    } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
+
+    mPendingTransactionCount.fetch_sub(transactions.size());
+    ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+    return transactions;
+}
+
+TransactionHandler::TransactionReadiness TransactionHandler::applyFilters(
+        TransactionFlushState& flushState) {
+    auto ready = TransactionReadiness::Ready;
+    for (auto& filter : mTransactionReadyFilters) {
+        auto perFilterReady = filter(flushState);
+        switch (perFilterReady) {
+            case TransactionReadiness::NotReady:
+            case TransactionReadiness::NotReadyBarrier:
+                return perFilterReady;
+
+            case TransactionReadiness::ReadyUnsignaled:
+            case TransactionReadiness::ReadyUnsignaledSingle:
+                // If one of the filters allows latching an unsignaled buffer, latch this ready
+                // state.
+                ready = perFilterReady;
+                break;
+            case TransactionReadiness::Ready:
+                continue;
+        }
+    }
+    return ready;
+}
+
+int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions,
+                                                      TransactionFlushState& flushState) {
+    int transactionsPendingBarrier = 0;
+    auto it = mPendingTransactionQueues.begin();
+    while (it != mPendingTransactionQueues.end()) {
+        auto& queue = it->second;
+        IBinder* queueToken = it->first.get();
+
+        // if we have already flushed a transaction with an unsignaled buffer then stop queue
+        // processing
+        if (std::find(flushState.queuesWithUnsignaledBuffers.begin(),
+                      flushState.queuesWithUnsignaledBuffers.end(),
+                      queueToken) != flushState.queuesWithUnsignaledBuffers.end()) {
+            continue;
+        }
+
+        while (!queue.empty()) {
+            auto& transaction = queue.front();
+            flushState.transaction = &transaction;
+            auto ready = applyFilters(flushState);
+            if (ready == TransactionReadiness::NotReadyBarrier) {
+                transactionsPendingBarrier++;
+                break;
+            } else if (ready == TransactionReadiness::NotReady) {
+                break;
+            }
+
+            // Transaction is ready move it from the pending queue.
+            flushState.firstTransaction = false;
+            removeFromStalledTransactions(transaction.id);
+            transactions.emplace_back(std::move(transaction));
+            queue.pop();
+
+            // If the buffer is unsignaled, then we don't want to signal other transactions using
+            // the buffer as a barrier.
+            auto& readyToApplyTransaction = transactions.back();
+            if (ready == TransactionReadiness::Ready) {
+                readyToApplyTransaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+                    const bool frameNumberChanged = state.bufferData->flags.test(
+                            BufferData::BufferDataChange::frameNumberChanged);
+                    if (frameNumberChanged) {
+                        flushState.bufferLayersReadyToPresent
+                                .emplace_or_replace(state.surface.get(),
+                                                    state.bufferData->frameNumber);
+                    } else {
+                        // Barrier function only used for BBQ which always includes a frame number.
+                        // This value only used for barrier logic.
+                        flushState.bufferLayersReadyToPresent
+                                .emplace_or_replace(state.surface.get(),
+                                                    std::numeric_limits<uint64_t>::max());
+                    }
+                });
+            } else if (ready == TransactionReadiness::ReadyUnsignaledSingle) {
+                // Track queues with a flushed unsingaled buffer.
+                flushState.queuesWithUnsignaledBuffers.emplace_back(queueToken);
+                break;
+            }
+        }
+
+        if (queue.empty()) {
+            it = mPendingTransactionQueues.erase(it);
+        } else {
+            it = std::next(it, 1);
+        }
+    }
+    return transactionsPendingBarrier;
+}
+
+void TransactionHandler::addTransactionReadyFilter(TransactionFilter&& filter) {
+    mTransactionReadyFilters.emplace_back(std::move(filter));
+}
+
+bool TransactionHandler::hasPendingTransactions() {
+    return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+}
+
+void TransactionHandler::onTransactionQueueStalled(const TransactionState& transaction,
+                                                   sp<ITransactionCompletedListener>& listener) {
+    if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transaction.id) !=
+        mStalledTransactions.end()) {
+        return;
+    }
+
+    mStalledTransactions.push_back(transaction.id);
+    listener->onTransactionQueueStalled();
+}
+
+void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
+    auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
+    if (it != mStalledTransactions.end()) {
+        mStalledTransactions.erase(it);
+    }
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/TransactionHandler.h b/services/surfaceflinger/TransactionHandler.h
new file mode 100644
index 0000000..237b48d
--- /dev/null
+++ b/services/surfaceflinger/TransactionHandler.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <semaphore.h>
+#include <cstdint>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <vector>
+
+#include <LocklessQueue.h>
+#include <TransactionState.h>
+#include <android-base/thread_annotations.h>
+#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
+
+namespace android {
+class TransactionHandler {
+public:
+    struct TransactionFlushState {
+        const TransactionState* transaction;
+        bool firstTransaction = true;
+        nsecs_t queueProcessTime = 0;
+        // Layer handles that have transactions with buffers that are ready to be applied.
+        ftl::SmallMap<IBinder* /* binder address */, uint64_t /* framenumber */, 15>
+                bufferLayersReadyToPresent = {};
+        ftl::SmallVector<IBinder* /* queueToken */, 15> queuesWithUnsignaledBuffers;
+    };
+    enum class TransactionReadiness {
+        NotReady,
+        NotReadyBarrier,
+        Ready,
+        ReadyUnsignaled,
+        ReadyUnsignaledSingle,
+    };
+    using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>;
+
+    bool hasPendingTransactions();
+    std::vector<TransactionState> flushTransactions();
+    void addTransactionReadyFilter(TransactionFilter&&);
+    void queueTransaction(TransactionState&&);
+    void onTransactionQueueStalled(const TransactionState&, sp<ITransactionCompletedListener>&);
+    void removeFromStalledTransactions(uint64_t transactionId);
+
+private:
+    // For unit tests
+    friend class TestableSurfaceFlinger;
+
+    int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
+    TransactionReadiness applyFilters(TransactionFlushState&);
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+            mPendingTransactionQueues;
+    LocklessQueue<TransactionState> mLocklessTransactionQueue;
+    std::atomic<size_t> mPendingTransactionCount = 0;
+    ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
+    std::vector<uint64_t> mStalledTransactions;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 61f0fa6..3cbfe81 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -26,8 +26,6 @@
 
 namespace android {
 
-class CountDownLatch;
-
 struct TransactionState {
     TransactionState() = default;
 
@@ -66,6 +64,15 @@
         }
     }
 
+    template <typename Visitor>
+    void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) const {
+        for (const auto& [state] : states) {
+            if (state.hasBufferChanges() && state.hasValidBuffer() && state.surface) {
+                if (!visitor(state)) return;
+            }
+        }
+    }
+
     // TODO(b/185535769): Remove FrameHint. Instead, reset the idle timer (of the relevant physical
     // display) on the main thread if commit leads to composite. Then, RefreshRateOverlay should be
     // able to setFrameRate once, rather than for each transaction.
@@ -97,49 +104,7 @@
     int originPid;
     int originUid;
     uint64_t id;
-    std::shared_ptr<CountDownLatch> transactionCommittedSignal;
     bool sentFenceTimeoutWarning = false;
 };
 
-class CountDownLatch {
-public:
-    enum {
-        eSyncTransaction = 1 << 0,
-    };
-    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::nanoseconds& 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/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h
new file mode 100644
index 0000000..3761f9e
--- /dev/null
+++ b/services/surfaceflinger/Utils/Dumper.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include <string_view>
+
+namespace android::utils {
+
+// Dumps variables by appending their name and value to the output string. A variable is formatted
+// as "name=value". If the name or value is empty, the format is "value" or "name=", respectively.
+// A value of user-defined type T is stringified via `std::string to_string(const T&)`, which must
+// be defined in the same namespace as T per the rules of ADL (argument-dependent lookup).
+//
+// TODO(b/249828573): Consolidate with <compositionengine/impl/DumpHelpers.h>
+class Dumper {
+public:
+    explicit Dumper(std::string& out) : mOut(out) {}
+
+    void eol() { mOut += '\n'; }
+
+    void dump(std::string_view name, std::string_view value = {}) {
+        using namespace std::string_view_literals;
+
+        for (int i = mIndent; i-- > 0;) mOut += "    "sv;
+        mOut += name;
+        if (!name.empty()) mOut += '=';
+        mOut += value;
+        eol();
+    }
+
+    void dump(std::string_view name, bool value) {
+        using namespace std::string_view_literals;
+        dump(name, value ? "true"sv : "false"sv);
+    }
+
+    template <typename T>
+    void dump(std::string_view name, const std::optional<T>& value) {
+        using namespace std::string_view_literals;
+        using std::to_string;
+        dump(name, value ? to_string(*value) : "nullopt"sv);
+    }
+
+    struct Indent {
+        explicit Indent(Dumper& dumper) : dumper(dumper) { dumper.mIndent++; }
+        ~Indent() { dumper.mIndent--; }
+
+        Dumper& dumper;
+    };
+
+private:
+    std::string& mOut;
+    int mIndent = 0;
+};
+
+} // namespace android::utils
diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md
index 7a5f229..a06c41b 100644
--- a/services/surfaceflinger/fuzzer/README.md
+++ b/services/surfaceflinger/fuzzer/README.md
@@ -78,9 +78,8 @@
 Layer supports the following parameters:
 1. Display Connection Types (parameter name: `fakeDisplay`)
 2. State Sets (parameter name: `traverseInZOrder`)
-3. State Subsets (parameter name: `prepareCompositionState`)
-4. Disconnect modes (parameter name: `disconnect`)
-5. Data Spaces (parameter name: `setDataspace`)
+3. Disconnect modes (parameter name: `disconnect`)
+4. Data Spaces (parameter name: `setDataspace`)
 
 You can find the possible values in the fuzzer's source code.
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index fae9165..f8fc6f5 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -116,7 +116,7 @@
 class DisplayHardwareFuzzer {
 public:
     DisplayHardwareFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
-        mPhysicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value_or(
+        mPhysicalDisplayId = TestableSurfaceFlinger::getFirstDisplayId().value_or(
                 PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint8_t>()));
     };
     void process();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index 28b875a..22d80ca 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -130,7 +130,7 @@
     mFlinger->maxFrameBufferAcquiredBuffers = mFdp.ConsumeIntegral<int64_t>();
     mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>();
     mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>();
-    mFlinger->hasWideColorDisplay = mFdp.ConsumeBool();
+    mTestableFlinger.mutableSupportsWideColor() = mFdp.ConsumeBool();
     mFlinger->useContextPriority = mFdp.ConsumeBool();
 
     mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces);
@@ -237,7 +237,8 @@
 
     mTestableFlinger.enableHalVirtualDisplays(mFdp.ConsumeBool());
 
-    mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>());
+    FTL_FAKE_GUARD(kMainThreadContext,
+                   mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>()));
 
     mTestableFlinger.notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>());
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index b25d959..ed2fe23 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -30,10 +30,8 @@
 #include <ui/DisplayStatInfo.h>
 #include <ui/DynamicDisplayInfo.h>
 
-#include "BufferStateLayer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
-#include "EffectLayer.h"
 #include "FrameTimeline/FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
@@ -48,7 +46,6 @@
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerDefaultFactory.h"
-#include "SurfaceInterceptor.h"
 #include "ThreadContext.h"
 #include "TimeStats/TimeStats.h"
 
@@ -62,7 +59,6 @@
 #include "tests/unittests/mock/MockFrameTimeline.h"
 #include "tests/unittests/mock/MockFrameTracer.h"
 #include "tests/unittests/mock/MockNativeWindowSurface.h"
-#include "tests/unittests/mock/MockSurfaceInterceptor.h"
 #include "tests/unittests/mock/MockTimeStats.h"
 #include "tests/unittests/mock/MockVSyncTracker.h"
 #include "tests/unittests/mock/MockVsyncController.h"
@@ -278,6 +274,7 @@
 
 private:
     // ICompositor overrides:
+    void configure() override {}
     bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
     void composite(TimePoint, VsyncId) override {}
     void sample() override {}
@@ -317,10 +314,6 @@
         return nullptr;
     }
 
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return sp<android::impl::SurfaceInterceptor>::make();
-    }
-
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
         return sp<StartPropertySetThread>::make(timestampPropertyValue);
     }
@@ -355,12 +348,10 @@
         return compositionengine::impl::createCompositionEngine();
     }
 
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs &) override {
-        return nullptr;
-    }
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs &) override { return nullptr; }
 
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs &args) override {
-        return sp<EffectLayer>::make(args);
+    sp<Layer> createEffectLayer(const LayerCreationArgs &args) override {
+        return sp<Layer>::make(args);
     }
 
     std::unique_ptr<FrameTracer> createFrameTracer() override {
@@ -445,9 +436,6 @@
         mFlinger->dumpFrameTimeline(dumpArgs, result);
 
         result = fdp->ConsumeRandomLengthString().c_str();
-        mFlinger->dumpStaticScreenStats(result);
-
-        result = fdp->ConsumeRandomLengthString().c_str();
         mFlinger->dumpRawDisplayIdentificationData(dumpArgs, result);
 
         LayersProto layersProto = mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>());
@@ -461,10 +449,6 @@
         mFlinger->calculateColorMatrix(fdp->ConsumeFloatingPoint<float>());
         mFlinger->updateColorMatrixLocked();
         mFlinger->CheckTransactCodeCredentials(fdp->ConsumeIntegral<uint32_t>());
-
-        const CountDownLatch transactionCommittedSignal(fdp->ConsumeIntegral<uint32_t>());
-        mFlinger->waitForSynchronousTransaction(transactionCommittedSignal);
-        mFlinger->signalSynchronousTransactions(fdp->ConsumeIntegral<uint32_t>());
     }
 
     void getCompositionPreference() {
@@ -532,6 +516,13 @@
         mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
     }
 
+    // TODO(b/248317436): extend to cover all displays for multi-display devices
+    static std::optional<PhysicalDisplayId> getFirstDisplayId() {
+        std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        if (ids.empty()) return {};
+        return ids.front();
+    }
+
     sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
         mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
         const sp<Client> client = sp<Client>::make(mFlinger);
@@ -543,9 +534,8 @@
         ui::PixelFormat pixelFormat{};
         mFlinger->getHwComposer().allocateVirtualDisplay(halVirtualDisplayId, uiSize, &pixelFormat);
 
-        PhysicalDisplayId physicalDisplayId =
-                SurfaceComposerClient::getInternalDisplayId().value_or(
-                        PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>()));
+        PhysicalDisplayId physicalDisplayId = getFirstDisplayId().value_or(
+                PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>()));
         mFlinger->getHwComposer().allocatePhysicalDisplay(kHwDisplayId, physicalDisplayId);
 
         sp<IBinder> display =
@@ -586,8 +576,6 @@
 
         onPullAtom(&mFdp);
 
-        mFlinger->injectVSync(mFdp.ConsumeIntegral<nsecs_t>());
-
         getCompositionPreference();
         getDisplayedContentSample(display, &mFdp);
         getDesiredDisplayModeSpecs(display);
@@ -602,14 +590,18 @@
         mFlinger->binderDied(display);
         mFlinger->onFirstRef();
 
-        mFlinger->commitTransactions();
         mFlinger->updateInputFlinger();
         mFlinger->updateCursorAsync();
 
         setVsyncConfig(&mFdp);
 
-        FTL_FAKE_GUARD(kMainThreadContext,
-                       mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)));
+        {
+            ftl::FakeGuard guard(kMainThreadContext);
+
+            mFlinger->commitTransactions();
+            mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
+            mFlinger->postComposition();
+        }
 
         mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
         mFlinger->clearTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -626,8 +618,6 @@
 
         mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>());
 
-        FTL_FAKE_GUARD(kMainThreadContext, mFlinger->postComposition());
-
         mFlinger->calculateExpectedPresentTime({});
 
         mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool());
@@ -666,7 +656,8 @@
         }
 
         mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
-        const auto fps = mRefreshRateConfigs->getActiveMode()->getFps();
+        const auto fps =
+                FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateConfigs->getActiveMode().getFps());
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -717,9 +708,9 @@
 
     void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); }
 
-    auto commitTransactionsLocked(uint32_t transactionFlags) {
+    void commitTransactionsLocked(uint32_t transactionFlags) FTL_FAKE_GUARD(kMainThreadContext) {
         Mutex::Autolock lock(mFlinger->mStateLock);
-        return mFlinger->commitTransactionsLocked(transactionFlags);
+        mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
     auto setDisplayStateLocked(const DisplayState &s) {
@@ -735,8 +726,10 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto &getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
-    auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+    auto &getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+    auto &getPendingTransactionQueue() {
+        return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+    }
 
     auto setTransactionState(
             const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states,
@@ -764,11 +757,10 @@
     /* Read-write access to private data to set up preconditions and assert
      * post-conditions.
      */
-
-    auto &mutableCurrentState() { return mFlinger->mCurrentState; }
-    auto &mutableDisplays() { return mFlinger->mDisplays; }
-    auto &mutableDrawingState() { return mFlinger->mDrawingState; }
-    auto &mutableInterceptor() { return mFlinger->mInterceptor; }
+    auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
+    auto& mutableCurrentState() { return mFlinger->mCurrentState; }
+    auto& mutableDisplays() { return mFlinger->mDisplays; }
+    auto& mutableDrawingState() { return mFlinger->mDrawingState; }
 
     auto fromHandle(const sp<IBinder> &handle) { return mFlinger->fromHandle(handle); }
 
@@ -776,7 +768,6 @@
         mutableDisplays().clear();
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
-        mutableInterceptor().clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
         mFlinger->mCompositionEngine->setRenderEngine(
@@ -785,7 +776,7 @@
 
 private:
     void setVsyncEnabled(bool) override {}
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+    void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index aeccc52..0a142c3 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -14,10 +14,8 @@
  * limitations under the License.
  *
  */
-#include <BufferStateLayer.h>
 #include <Client.h>
 #include <DisplayDevice.h>
-#include <EffectLayer.h>
 #include <LayerRenderArea.h>
 #include <ftl/future.h>
 #include <fuzzer/FuzzedDataProvider.h>
@@ -79,13 +77,13 @@
     TestableSurfaceFlinger flinger;
     sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger()));
     const LayerCreationArgs layerCreationArgs = createLayerCreationArgs(&flinger, client);
-    sp<EffectLayer> effectLayer = sp<EffectLayer>::make(layerCreationArgs);
+    sp<Layer> effectLayer = sp<Layer>::make(layerCreationArgs);
 
     effectLayer->setColor({(mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*x*/,
                             mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*y*/,
                             mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*z*/)});
     effectLayer->setDataspace(mFdp.PickValueInArray(kDataspaces));
-    sp<EffectLayer> parent = sp<EffectLayer>::make(layerCreationArgs);
+    sp<Layer> parent = sp<Layer>::make(layerCreationArgs);
     effectLayer->setChildrenDrawingParent(parent);
 
     const FrameTimelineInfo frameInfo = getFuzzedFrameTimelineInfo();
@@ -110,8 +108,7 @@
 void LayerFuzzer::invokeBufferStateLayer() {
     TestableSurfaceFlinger flinger;
     sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger()));
-    sp<BufferStateLayer> layer =
-            sp<BufferStateLayer>::make(createLayerCreationArgs(&flinger, client));
+    sp<Layer> layer = sp<Layer>::make(createLayerCreationArgs(&flinger, client));
     sp<Fence> fence = sp<Fence>::make();
     const std::shared_ptr<FenceTime> fenceTime = std::make_shared<FenceTime>(fence);
 
@@ -149,7 +146,8 @@
     layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
 
     layer->fenceHasSignaled();
-    layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>());
+    layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>(),
+                            false /*updatingOutputGeometryThisFrame*/);
     const std::vector<sp<CallbackHandle>> callbacks;
     layer->setTransactionCompletedListeners(callbacks);
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index f507ef0..2614288 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -92,7 +92,7 @@
     const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
     std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
             android::impl::EventThread>(std::move(std::make_unique<FuzzImplVSyncSource>()), nullptr,
-                                        nullptr, nullptr, getVsyncPeriod);
+                                        nullptr, getVsyncPeriod);
 
     thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
     sp<EventThreadConnection> connection =
@@ -322,7 +322,7 @@
     LayerCreationArgs args(flinger.flinger(), client,
                            mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/,
                            mFdp.ConsumeIntegral<uint16_t>() /*layerFlags*/, LayerMetadata());
-    sp<Layer> layer = sp<BufferStateLayer>::make(args);
+    sp<Layer> layer = sp<Layer>::make(args);
 
     layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>());
 }
@@ -350,7 +350,7 @@
     const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
     std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
 
-    refreshRateConfigs.getBestRefreshRate(layers, globalSignals);
+    refreshRateConfigs.getRankedRefreshRates(layers, globalSignals);
 
     layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
     layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
@@ -362,11 +362,24 @@
                                                              mFdp.ConsumeFloatingPoint<float>()),
                                                      globalSignals);
 
-    refreshRateConfigs.setDisplayManagerPolicy(
-            {modeId,
-             {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
-              Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
-    refreshRateConfigs.setActiveModeId(modeId);
+    {
+        ftl::FakeGuard guard(kMainThreadContext);
+
+        refreshRateConfigs.setPolicy(
+                RefreshRateConfigs::
+                        DisplayManagerPolicy{modeId,
+                                             {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+                                              Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
+        refreshRateConfigs.setPolicy(
+                RefreshRateConfigs::OverridePolicy{modeId,
+                                                   {Fps::fromValue(
+                                                            mFdp.ConsumeFloatingPoint<float>()),
+                                                    Fps::fromValue(
+                                                            mFdp.ConsumeFloatingPoint<float>())}});
+        refreshRateConfigs.setPolicy(RefreshRateConfigs::NoOverridePolicy{});
+
+        refreshRateConfigs.setActiveModeId(modeId);
+    }
 
     RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue(
                                                            mFdp.ConsumeFloatingPoint<float>()),
@@ -379,7 +392,8 @@
     RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
                                       PowerMode::OFF);
 
-    const auto fpsOpt = displayModes.get(modeId, [](const auto& mode) { return mode->getFps(); });
+    const auto fpsOpt = displayModes.get(modeId).transform(
+            [](const DisplayModePtr& mode) { return mode->getFps(); });
     refreshRateStats.setRefreshRate(*fpsOpt);
 
     refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes));
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index 973a439..7287dd0 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -44,10 +44,11 @@
 }
 
 java_library_static {
-    name: "layersprotosnano",
+    name: "layersprotoslite",
     host_supported: true,
     proto: {
-        type: "nano",
+        type: "lite",
+        include_dirs: ["external/protobuf/src"],
     },
     srcs: ["*.proto"],
     sdk_version: "core_platform",
@@ -56,7 +57,7 @@
             jarjar_rules: "jarjar-rules.txt",
         },
         host: {
-            static_libs: ["libprotobuf-java-nano"],
+            static_libs: ["libprotobuf-java-lite"],
         },
     },
 }
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index 49487ee..4c6a9cf 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -82,7 +82,7 @@
         eChangesLsbNone = 0;
         ePositionChanged = 0x00000001;
         eLayerChanged = 0x00000002;
-        eSizeChanged = 0x00000004;
+        // unused = 0x00000004;
         eAlphaChanged = 0x00000008;
 
         eMatrixChanged = 0x00000010;
@@ -98,8 +98,7 @@
         eReparent = 0x00008000;
 
         eColorChanged = 0x00010000;
-        eDestroySurface = 0x00020000;
-        eTransformChanged = 0x00040000;
+        eBufferTransformChanged = 0x00040000;
         eTransformToDisplayInverseChanged = 0x00080000;
 
         eCropChanged = 0x00100000;
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 276431e..bd6367d 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -23,7 +23,10 @@
 
 cc_test {
     name: "SurfaceFlinger_test",
-    defaults: ["surfaceflinger_defaults"],
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+        "surfaceflinger_defaults",
+    ],
     test_suites: ["device-tests"],
     srcs: [
         "BootDisplayMode_test.cpp",
@@ -53,18 +56,15 @@
         "SetFrameRateOverride_test.cpp",
         "SetGeometry_test.cpp",
         "Stress_test.cpp",
-        "SurfaceInterceptor_test.cpp",
         "VirtualDisplay_test.cpp",
         "WindowInfosListener_test.cpp",
     ],
     data: ["SurfaceFlinger_test.filter"],
     static_libs: [
-        "libtrace_proto",
         "liblayers_proto",
         "android.hardware.graphics.composer@2.1",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.common@1.2",
         "libandroid",
         "libbase",
@@ -126,7 +126,6 @@
 }
 
 subdirs = [
-    "fakehwc",
     "hwc2",
     "unittests",
     "utils",
diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
index 432e227..f2874ae 100644
--- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp
+++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
@@ -30,7 +30,10 @@
 
 TEST(BootDisplayModeTest, setBootDisplayMode) {
     sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-    auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     bool bootModeSupport = false;
     binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
     ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
@@ -42,7 +45,9 @@
 
 TEST(BootDisplayModeTest, clearBootDisplayMode) {
     sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-    auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     bool bootModeSupport = false;
     binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
     ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 353b813..4f04934 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -55,19 +55,12 @@
 #pragma clang diagnostic ignored "-Wconversion"
 class CredentialsTest : public ::testing::Test {
 protected:
-    void SetUp() override {
-        // Start the tests as root.
-        seteuid(AID_ROOT);
-
-        ASSERT_NO_FATAL_FAILURE(initClient());
-    }
+    void SetUp() override { ASSERT_NO_FATAL_FAILURE(initClient()); }
 
     void TearDown() override {
         mComposerClient->dispose();
         mBGSurfaceControl.clear();
         mComposerClient.clear();
-        // Finish the tests as root.
-        seteuid(AID_ROOT);
     }
 
     sp<IBinder> mDisplay;
@@ -81,8 +74,17 @@
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
     }
 
+    static sp<IBinder> getFirstDisplayToken() {
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        if (ids.empty()) {
+            return nullptr;
+        }
+
+        return SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+    }
+
     void setupBackgroundSurface() {
-        mDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        mDisplay = getFirstDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr);
 
         ui::DisplayMode mode;
@@ -102,31 +104,6 @@
     }
 
     /**
-     * Sets UID to imitate Graphic's process.
-     */
-    void setGraphicsUID() {
-        seteuid(AID_ROOT);
-        seteuid(AID_GRAPHICS);
-    }
-
-    /**
-     * Sets UID to imitate System's process.
-     */
-    void setSystemUID() {
-        seteuid(AID_ROOT);
-        seteuid(AID_SYSTEM);
-    }
-
-    /**
-     * Sets UID to imitate a process that doesn't have any special privileges in
-     * our code.
-     */
-    void setBinUID() {
-        seteuid(AID_ROOT);
-        seteuid(AID_BIN);
-    }
-
-    /**
      * Template function the check a condition for different types of users: root
      * graphics, system, and non-supported user. Root, graphics, and system should
      * always equal privilegedValue, and non-supported user should equal unprivilegedValue.
@@ -134,24 +111,34 @@
     template <typename T>
     void checkWithPrivileges(std::function<T()> condition, T privilegedValue, T unprivilegedValue) {
         // Check with root.
-        seteuid(AID_ROOT);
-        ASSERT_EQ(privilegedValue, condition());
+        {
+            UIDFaker f(AID_SYSTEM);
+            ASSERT_EQ(privilegedValue, condition());
+        }
 
         // Check as a Graphics user.
-        setGraphicsUID();
-        ASSERT_EQ(privilegedValue, condition());
+        {
+            UIDFaker f(AID_GRAPHICS);
+            ASSERT_EQ(privilegedValue, condition());
+        }
 
         // Check as a system user.
-        setSystemUID();
-        ASSERT_EQ(privilegedValue, condition());
+        {
+            UIDFaker f(AID_SYSTEM);
+            ASSERT_EQ(privilegedValue, condition());
+        }
 
         // Check as a non-supported user.
-        setBinUID();
-        ASSERT_EQ(unprivilegedValue, condition());
+        {
+            UIDFaker f(AID_BIN);
+            ASSERT_EQ(unprivilegedValue, condition());
+        }
 
         // Check as shell since shell has some additional permissions
-        seteuid(AID_SHELL);
-        ASSERT_EQ(unprivilegedValue, condition());
+        {
+            UIDFaker f(AID_SHELL);
+            ASSERT_EQ(privilegedValue, condition());
+        }
     }
 };
 
@@ -160,23 +147,27 @@
     ASSERT_NO_FATAL_FAILURE(initClient());
 
     // Graphics can init the client.
-    setGraphicsUID();
-    ASSERT_NO_FATAL_FAILURE(initClient());
+    {
+        UIDFaker f(AID_GRAPHICS);
+        ASSERT_NO_FATAL_FAILURE(initClient());
+    }
 
     // System can init the client.
-    setSystemUID();
-    ASSERT_NO_FATAL_FAILURE(initClient());
+    {
+        UIDFaker f(AID_SYSTEM);
+        ASSERT_NO_FATAL_FAILURE(initClient());
+    }
 
     // Anyone else can init the client.
-    setBinUID();
-    mComposerClient = sp<SurfaceComposerClient>::make();
-    ASSERT_NO_FATAL_FAILURE(initClient());
+    {
+        UIDFaker f(AID_BIN);
+        mComposerClient = sp<SurfaceComposerClient>::make();
+        ASSERT_NO_FATAL_FAILURE(initClient());
+    }
 }
 
 TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
-    std::function<bool()> condition = [] {
-        return SurfaceComposerClient::getInternalDisplayToken() != nullptr;
-    };
+    std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; };
     // Anyone can access display information.
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
 }
@@ -184,8 +175,8 @@
 TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
     // The following methods are tested with a UID that is not root, graphics,
     // or system, to show that anyone can access them.
-    setBinUID();
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    UIDFaker f(AID_BIN);
+    const auto display = getFirstDisplayToken();
     ASSERT_TRUE(display != nullptr);
 
     ui::DisplayMode mode;
@@ -197,7 +188,7 @@
 }
 
 TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         ui::DynamicDisplayInfo info;
         return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
@@ -206,7 +197,7 @@
 }
 
 TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         ui::DisplayPrimaries primaries;
         return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
@@ -215,7 +206,7 @@
 }
 
 TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ui::DisplayModeId defaultMode;
     bool allowGroupSwitching;
     float primaryFpsMin;
@@ -238,7 +229,7 @@
 }
 
 TEST_F(CredentialsTest, SetActiveColorModeTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE);
     };
@@ -253,24 +244,34 @@
     };
 
     // Check with root.
-    seteuid(AID_ROOT);
-    ASSERT_FALSE(condition());
+    {
+        UIDFaker f(AID_ROOT);
+        ASSERT_FALSE(condition());
+    }
 
     // Check as a Graphics user.
-    setGraphicsUID();
-    ASSERT_TRUE(condition());
+    {
+        UIDFaker f(AID_GRAPHICS);
+        ASSERT_TRUE(condition());
+    }
 
     // Check as a system user.
-    setSystemUID();
-    ASSERT_TRUE(condition());
+    {
+        UIDFaker f(AID_SYSTEM);
+        ASSERT_TRUE(condition());
+    }
 
     // Check as a non-supported user.
-    setBinUID();
-    ASSERT_FALSE(condition());
+    {
+        UIDFaker f(AID_BIN);
+        ASSERT_FALSE(condition());
+    }
 
     // Check as shell since shell has some additional permissions
-    seteuid(AID_SHELL);
-    ASSERT_FALSE(condition());
+    {
+        UIDFaker f(AID_SHELL);
+        ASSERT_FALSE(condition());
+    }
 
     condition = [=]() {
         sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
@@ -280,7 +281,7 @@
 }
 
 TEST_F(CredentialsTest, CaptureTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         sp<GraphicBuffer> outBuffer;
         DisplayCaptureArgs captureArgs;
@@ -315,25 +316,31 @@
     // Historically, only root and shell can access the getLayerDebugInfo which
     // is called when we call dumpsys. I don't see a reason why we should change this.
     std::vector<LayerDebugInfo> outLayers;
+    binder::Status status = binder::Status::ok();
     // Check with root.
-    seteuid(AID_ROOT);
-    binder::Status status = sf->getLayerDebugInfo(&outLayers);
-    ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+    {
+        UIDFaker f(AID_ROOT);
+        status = sf->getLayerDebugInfo(&outLayers);
+        ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+    }
 
     // Check as a shell.
-    seteuid(AID_SHELL);
-    status = sf->getLayerDebugInfo(&outLayers);
-    ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+    {
+        UIDFaker f(AID_SHELL);
+        status = sf->getLayerDebugInfo(&outLayers);
+        ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+    }
 
     // Check as anyone else.
-    seteuid(AID_ROOT);
-    seteuid(AID_BIN);
-    status = sf->getLayerDebugInfo(&outLayers);
-    ASSERT_EQ(PERMISSION_DENIED, statusTFromBinderStatus(status));
+    {
+        UIDFaker f(AID_BIN);
+        status = sf->getLayerDebugInfo(&outLayers);
+        ASSERT_EQ(PERMISSION_DENIED, statusTFromBinderStatus(status));
+    }
 }
 
 TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_FALSE(display == nullptr);
     bool result = false;
     status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
@@ -357,7 +364,7 @@
 }
 
 TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_FALSE(display == nullptr);
     std::function<status_t()> condition = [=]() {
         bool result = false;
@@ -367,7 +374,7 @@
 }
 
 TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_FALSE(display == nullptr);
     ui::DynamicDisplayInfo info;
     SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 2dc96b8..02c934e 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -48,7 +48,9 @@
 
 protected:
     void SetUp() override {
-        mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         status_t res =
                 SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken,
                                                                   &initialDefaultMode,
@@ -149,4 +151,4 @@
 } // namespace android
 
 // 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/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 9fa0452..52aa502 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -28,7 +28,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -177,7 +179,9 @@
 }
 
 TEST_F(EffectLayerTest, EffectLayerWithColorNoCrop) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
     const ui::Size& resolution = mode.resolution;
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index c63d251..40a5d57 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -224,7 +224,9 @@
         mClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        mPrimaryDisplay = mClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mPrimaryDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ui::DisplayMode mode;
         mClient->getActiveDisplayMode(mPrimaryDisplay, &mode);
         mDisplayWidth = mode.resolution.getWidth();
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 741b6f7..666ce76 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -48,7 +48,7 @@
     }
 
     sp<SurfaceControl> makeNotSurfaceControl() {
-        return sp<SurfaceControl>::make(mScc, sp<NotALayer>::make(), 1);
+        return sp<SurfaceControl>::make(mScc, sp<NotALayer>::make(), 1, "#1");
     }
 };
 
diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp
index 0d55ec1..00e134b 100644
--- a/services/surfaceflinger/tests/LayerBorder_test.cpp
+++ b/services/surfaceflinger/tests/LayerBorder_test.cpp
@@ -34,7 +34,9 @@
         toHalf3 = ColorTransformHelper::toHalf3;
         toHalf4 = ColorTransformHelper::toHalf4;
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
         mColorOrange = toHalf4({255, 140, 0, 255});
         mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -215,13 +217,13 @@
     });
 }
 
-TEST_F(LayerBorderTest, BufferStateLayer) {
+TEST_F(LayerBorderTest, LayerWithBuffer) {
     asTransaction([&](Transaction& t) {
         t.hide(mEffectLayer1);
         t.hide(mEffectLayer2);
         t.show(mContainerLayer);
 
-        sp<SurfaceControl> bufferStateLayer =
+        sp<SurfaceControl> layer =
                 mClient->createSurface(String8("BufferState"), 0 /* width */, 0 /* height */,
                                        PIXEL_FORMAT_RGBA_8888,
                                        ISurfaceComposerClient::eFXSurfaceBufferState,
@@ -236,9 +238,9 @@
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 200, 200), Color::GREEN);
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(200, 200, 400, 400), Color::BLUE);
 
-        t.setBuffer(bufferStateLayer, buffer);
-        t.setPosition(bufferStateLayer, 100, 100);
-        t.show(bufferStateLayer);
+        t.setBuffer(layer, buffer);
+        t.setPosition(layer, 100, 100);
+        t.show(layer);
         t.enableBorder(mContainerLayer, true, 20, mColorOrange);
     });
 }
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 1460fe1..26dbc76 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -51,7 +51,7 @@
         LayerTransactionTest::TearDown();
     }
 
-    virtual sp<SurfaceControl> createBufferStateLayer() {
+    virtual sp<SurfaceControl> createLayerWithBuffer() {
         return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
     }
 
@@ -164,7 +164,7 @@
 
 TEST_F(LayerCallbackTest, BufferColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -183,7 +183,7 @@
 
 TEST_F(LayerCallbackTest, NoBufferNoColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -206,7 +206,7 @@
 
 TEST_F(LayerCallbackTest, BufferNoColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -228,7 +228,7 @@
 
 TEST_F(LayerCallbackTest, NoBufferColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -266,7 +266,7 @@
 
 TEST_F(LayerCallbackTest, OffScreen) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -288,8 +288,8 @@
 
 TEST_F(LayerCallbackTest, MergeBufferNoColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -322,8 +322,8 @@
 
 TEST_F(LayerCallbackTest, MergeNoBufferColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -357,8 +357,8 @@
 
 TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -392,8 +392,8 @@
 }
 TEST_F(LayerCallbackTest, Merge_SameCallback) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback;
@@ -418,7 +418,7 @@
 
 TEST_F(LayerCallbackTest, Merge_SameLayer) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -485,7 +485,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -510,7 +510,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -541,7 +541,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -579,8 +579,8 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -799,7 +799,7 @@
 // TODO (b/183181768): Fix & re-enable
 TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -823,7 +823,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     // Normal call to set up test
     Transaction transaction;
@@ -858,7 +858,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     // Normal call to set up test
     Transaction transaction;
@@ -901,7 +901,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -925,7 +925,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback1;
@@ -971,7 +971,7 @@
 // TODO (b/183181768): Fix & re-enable
 TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback1;
@@ -1015,7 +1015,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1039,7 +1039,7 @@
 
 TEST_F(LayerCallbackTest, ExpectedPresentTime) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1065,8 +1065,8 @@
 // b202394221
 TEST_F(LayerCallbackTest, EmptyBufferStateChanges) {
     sp<SurfaceControl> bufferLayer, emptyBufferLayer;
-    ASSERT_NO_FATAL_FAILURE(bufferLayer = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1120,7 +1120,7 @@
 
 TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
     sp<SurfaceControl> offscreenLayer =
             createSurface(mClient, "Offscreen Layer", 0, 0, PIXEL_FORMAT_RGBA_8888,
                           ISurfaceComposerClient::eFXSurfaceBufferState, layer.get());
@@ -1151,7 +1151,7 @@
 
 TEST_F(LayerCallbackTest, TransactionCommittedCallback_BSL) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index bbe7ae8..bf7cae9 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -399,7 +399,7 @@
             .apply();
     ASSERT_NO_FATAL_FAILURE(
             fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layerR, Color::RED, 32, 32));
     getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
 }
 
@@ -482,7 +482,7 @@
     }
 }
 
-// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
+// RED: Color layer base color and Layer buffer fill
 // BLUE: prior background color
 // GREEN: final background color
 // BLACK: no color or fill
@@ -516,7 +516,7 @@
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
             if (bufferFill) {
-                ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
+                ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, fillColor, width, height));
                 expectedColor = fillColor;
             }
             Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
@@ -832,7 +832,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     const Rect crop(8, 8, 24, 24);
 
     Transaction().setCrop(layer, crop).apply();
@@ -863,7 +863,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("empty rect");
@@ -944,7 +944,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     const Rect crop(8, 8, 24, 24);
     Transaction().setPosition(layer, 32, 32).setCrop(layer, crop).apply();
@@ -972,7 +972,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     const Rect frame(8, 8, 24, 24);
 
     Transaction t;
@@ -988,7 +988,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     Transaction t;
     {
@@ -1014,7 +1014,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 10, 10));
 
     // A layer with a buffer will have a computed size that matches the buffer size.
     auto shot = getScreenCapture();
@@ -1026,11 +1026,11 @@
     sp<SurfaceControl> parent, child;
     ASSERT_NO_FATAL_FAILURE(
             parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(parent, Color::RED, 32, 32));
 
     ASSERT_NO_FATAL_FAILURE(
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
 
     Transaction().reparent(child, parent).apply();
 
@@ -1047,7 +1047,7 @@
 
     ASSERT_NO_FATAL_FAILURE(
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
 
     Transaction().reparent(child, parent).apply();
 
@@ -1061,7 +1061,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     std::this_thread::sleep_for(500ms);
 
@@ -1080,9 +1080,9 @@
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
     Transaction().reparent(child, parent).apply();
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(parent, Color::RED, 32, 32));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
     Rect childDst(0, 16, 32, 32);
     Transaction t;
     TransactionUtils::setFrame(t, child, Rect(0, 0, 10, 10), childDst);
@@ -1099,7 +1099,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
@@ -1111,7 +1111,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 1");
@@ -1120,7 +1120,7 @@
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLUE, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 2");
@@ -1129,7 +1129,7 @@
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 3");
@@ -1148,7 +1148,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer1, Color::RED, 64, 64));
 
     {
         SCOPED_TRACE("set layer 1 buffer red");
@@ -1156,7 +1156,7 @@
         shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer2, Color::BLUE, 32, 32));
 
     {
         SCOPED_TRACE("set layer 2 buffer blue");
@@ -1166,7 +1166,7 @@
         shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer1, Color::GREEN, 64, 64));
     {
         SCOPED_TRACE("set layer 1 buffer green");
         auto shot = getScreenCapture();
@@ -1175,7 +1175,7 @@
         shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer2, Color::WHITE, 32, 32));
 
     {
         SCOPED_TRACE("set layer 2 buffer white");
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index fbbf322..2181370 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -90,13 +90,12 @@
     ASSERT_EQ(args.grayscale, args2.grayscale);
 }
 
-TEST(LayerStateTest, ParcellingScreenCaptureResults) {
+TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFence) {
     ScreenCaptureResults results;
     results.buffer = sp<GraphicBuffer>::make(100u, 200u, PIXEL_FORMAT_RGBA_8888, 1u, 0u);
-    results.fence = sp<Fence>::make(dup(fileno(tmpfile())));
+    results.fenceResult = sp<Fence>::make(dup(fileno(tmpfile())));
     results.capturedSecureLayers = true;
     results.capturedDataspace = ui::Dataspace::DISPLAY_P3;
-    results.result = BAD_VALUE;
 
     Parcel p;
     results.writeToParcel(&p);
@@ -110,10 +109,41 @@
     ASSERT_EQ(results.buffer->getWidth(), results2.buffer->getWidth());
     ASSERT_EQ(results.buffer->getHeight(), results2.buffer->getHeight());
     ASSERT_EQ(results.buffer->getPixelFormat(), results2.buffer->getPixelFormat());
-    ASSERT_EQ(results.fence->isValid(), results2.fence->isValid());
+    ASSERT_TRUE(results.fenceResult.ok());
+    ASSERT_TRUE(results2.fenceResult.ok());
+    ASSERT_EQ(results.fenceResult.value()->isValid(), results2.fenceResult.value()->isValid());
     ASSERT_EQ(results.capturedSecureLayers, results2.capturedSecureLayers);
     ASSERT_EQ(results.capturedDataspace, results2.capturedDataspace);
-    ASSERT_EQ(results.result, results2.result);
+}
+
+TEST(LayerStateTest, ParcellingScreenCaptureResultsWithNoFenceOrError) {
+    ScreenCaptureResults results;
+
+    Parcel p;
+    results.writeToParcel(&p);
+    p.setDataPosition(0);
+
+    ScreenCaptureResults results2;
+    results2.readFromParcel(&p);
+
+    ASSERT_TRUE(results2.fenceResult.ok());
+    ASSERT_EQ(results2.fenceResult.value(), Fence::NO_FENCE);
+}
+
+TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFenceError) {
+    ScreenCaptureResults results;
+    results.fenceResult = base::unexpected(BAD_VALUE);
+
+    Parcel p;
+    results.writeToParcel(&p);
+    p.setDataPosition(0);
+
+    ScreenCaptureResults results2;
+    results2.readFromParcel(&p);
+
+    ASSERT_FALSE(results.fenceResult.ok());
+    ASSERT_FALSE(results2.fenceResult.ok());
+    ASSERT_EQ(results.fenceResult.error(), results2.fenceResult.error());
 }
 
 } // namespace test
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 4b91605..badd5be 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -137,8 +137,8 @@
         postBufferQueueLayerBuffer(layer);
     }
 
-    virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
-                                           int32_t bufferWidth, int32_t bufferHeight) {
+    virtual void fillBufferLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+                                      int32_t bufferWidth, int32_t bufferHeight) {
         sp<GraphicBuffer> buffer =
                 sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
                                         static_cast<uint32_t>(bufferHeight), PIXEL_FORMAT_RGBA_8888,
@@ -159,7 +159,7 @@
                 fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
                 break;
             case ISurfaceComposerClient::eFXSurfaceBufferState:
-                fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
+                fillBufferLayerColor(layer, color, bufferWidth, bufferHeight);
                 break;
             default:
                 ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
@@ -233,7 +233,7 @@
                                                  Rect(halfW, halfH, bufferWidth, bufferHeight),
                                                  bottomRight);
 
-        Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply();
+        Transaction().setBuffer(layer, buffer).apply();
     }
 
     std::unique_ptr<ScreenCapture> screenshot() {
@@ -289,7 +289,9 @@
 
 private:
     void SetUpDisplay() {
-        mDisplay = mClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
 
         ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 513fdc3..cbd54e7 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -32,7 +32,7 @@
 
     Transaction().setTransformToDisplayInverse(layer, false).apply();
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::GREEN, 32, 32));
 
     Transaction().setTransformToDisplayInverse(layer, true).apply();
 }
@@ -154,6 +154,36 @@
 
     ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
 }
+
+// b/245052266 - we possible could support blur and a buffer at the same layer but
+// might break existing assumptions at higher level. This test captures the current
+// expectations. A layer drawing a buffer will not support blur.
+TEST_F(LayerTransactionTest, BufferTakesPriorityOverBlur) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
+    Transaction().setBackgroundBlurRadius(layer, 5).apply();
+    {
+        SCOPED_TRACE("BufferTakesPriorityOverBlur");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+    }
+}
+
+TEST_F(LayerTransactionTest, BufferTakesPriorityOverColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
+    Transaction().setColor(layer, {Color::GREEN.r, Color::GREEN.g, Color::GREEN.b}).apply();
+    {
+        SCOPED_TRACE("BufferTakesPriorityOverColor");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index e1a7ecc..867eddb 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -33,7 +33,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index a921aa8..e69db7c 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -29,8 +29,10 @@
     virtual void SetUp() {
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -193,14 +195,14 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    sp<SurfaceControl> bufferStateLayer =
-            createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
+    sp<SurfaceControl> layer =
+            createLayer("Layer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
                         mChildLayer.get());
-    fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200);
-    Transaction().show(bufferStateLayer).apply();
+    fillBufferLayerColor(layer, Color::BLUE, 200, 200);
+    Transaction().show(layer).apply();
 
     {
-        SCOPED_TRACE("Initial Mirror BufferStateLayer");
+        SCOPED_TRACE("Initial Mirror Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
@@ -208,9 +210,9 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    fillBufferStateLayerColor(bufferStateLayer, Color::WHITE, 200, 200);
+    fillBufferLayerColor(layer, Color::WHITE, 200, 200);
     {
-        SCOPED_TRACE("Update BufferStateLayer");
+        SCOPED_TRACE("Update Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
@@ -218,9 +220,9 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    Transaction().reparent(bufferStateLayer, nullptr).apply();
+    Transaction().reparent(layer, nullptr).apply();
     {
-        SCOPED_TRACE("Removed BufferStateLayer");
+        SCOPED_TRACE("Removed Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
@@ -231,7 +233,10 @@
 
 // Test that the mirror layer is initially offscreen.
 TEST_F(MirrorLayerTest, InitialMirrorState) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     SurfaceComposerClient::getActiveDisplayMode(display, &mode);
     const ui::Size& size = mode.resolution;
@@ -275,7 +280,9 @@
 
 // Test that a mirror layer can be screenshot when offscreen
 TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     SurfaceComposerClient::getActiveDisplayMode(display, &mode);
     const ui::Size& size = mode.resolution;
@@ -283,7 +290,7 @@
     sp<SurfaceControl> grandchild =
             createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState,
                         mChildLayer.get());
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(grandchild, Color::BLUE, 50, 50));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(grandchild, Color::BLUE, 50, 50));
     Rect childBounds = Rect(50, 50, 450, 450);
 
     asTransaction([&](Transaction& t) {
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 1ed6c65..15ff696 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -35,7 +35,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
         SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
 
diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
index fb4458a..9162674 100644
--- a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
+++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
@@ -80,14 +80,6 @@
 
 } // namespace
 
-TEST(RefreshRateOverlayTest, enableOverlay) {
-    toggleOverlay(true);
-}
-
-TEST(RefreshRateOverlayTest, disableOverlay) {
-    toggleOverlay(false);
-}
-
 TEST(RefreshRateOverlayTest, enableAndDisableOverlay) {
     toggleOverlay(true);
     toggleOverlay(false);
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 50a4092..9cebf11 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -33,7 +33,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         // Back layer
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index d78c8a9..976ee35 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -30,7 +30,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         ui::DisplayMode mode;
@@ -371,7 +373,7 @@
     ScreenCaptureResults captureResults;
     ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::RED, 32, 32));
     SurfaceComposerClient::Transaction().apply(true);
     ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults));
     ScreenCapture sc(captureResults.buffer, captureResults.capturedHdrLayers);
@@ -449,8 +451,8 @@
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
                                                  redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -484,8 +486,8 @@
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
                                                  redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -549,8 +551,8 @@
                         ISurfaceComposerClient::eSecure |
                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                         redLayer.get());
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(secureLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(secureLayer, Color::BLUE, 30, 30));
 
     auto redLayerHandle = redLayer->getHandle();
     Transaction()
@@ -803,7 +805,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
 
     LayerCaptureArgs captureArgs;
@@ -825,7 +827,7 @@
     mCapture->expectColor(Rect(0, 0, 32, 32),
                           Color{expectedColor, expectedColor, expectedColor, 255}, tolerance);
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLUE, 32, 32));
     ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     expectedColor = luminance.b * 255;
@@ -838,7 +840,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply();
 
@@ -865,7 +867,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLACK, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLACK, 32, 32));
     Transaction()
             .show(layer)
             .setLayer(layer, INT32_MAX)
@@ -885,7 +887,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLACK, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLACK, 32, 32));
     Transaction()
             .show(layer)
             .setLayer(layer, INT32_MAX)
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
deleted file mode 100644
index 8dcd013..0000000
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ /dev/null
@@ -1,967 +0,0 @@
-/*
- * Copyright (C) 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"
-#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>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-#include <ui/DisplayMode.h>
-
-#include <fstream>
-#include <random>
-#include <thread>
-
-namespace android {
-
-using Transaction = SurfaceComposerClient::Transaction;
-using SurfaceChange = surfaceflinger::SurfaceChange;
-using Trace = surfaceflinger::Trace;
-using Increment = surfaceflinger::Increment;
-
-constexpr uint32_t BUFFER_UPDATES = 18;
-constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
-constexpr uint32_t SIZE_UPDATE = 134;
-constexpr uint32_t STACK_UPDATE = 1;
-constexpr int32_t RELATIVE_Z = 42;
-constexpr float ALPHA_UPDATE = 0.29f;
-constexpr float CORNER_RADIUS_UPDATE = 0.2f;
-constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24;
-constexpr float POSITION_UPDATE = 121;
-const Rect CROP_UPDATE(16, 16, 32, 32);
-const float SHADOW_RADIUS_UPDATE = 35.0f;
-std::vector<BlurRegion> BLUR_REGIONS_UPDATE;
-
-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 LAYER_NAME = "Layer Create and Delete Test";
-
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = sc->getSurface();
-    ASSERT_TRUE(s != nullptr);
-    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
-    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
-            pixel[0] = r;
-            pixel[1] = g;
-            pixel[2] = b;
-            pixel[3] = 255;
-        }
-    }
-    ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-}
-
-static status_t readProtoFile(Trace* trace) {
-    status_t err = NO_ERROR;
-
-    int fd = open(DEFAULT_FILENAME, O_RDONLY);
-    {
-        google::protobuf::io::FileInputStream f(fd);
-        if (fd && !trace->ParseFromZeroCopyStream(&f)) {
-            err = PERMISSION_DENIED;
-        }
-    }
-    close(fd);
-
-    return err;
-}
-
-static void enableInterceptor() {
-    system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
-}
-
-static void disableInterceptor() {
-    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() == getUniqueName(surfaceName, increment)) {
-                layerId = increment.surface_creation().id();
-            }
-        }
-    }
-    return layerId;
-}
-
-int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) {
-    int32_t displayId = 0;
-    for (const auto& increment : capturedTrace.increment()) {
-        if (increment.increment_case() == increment.kDisplayCreation) {
-            if (increment.display_creation().name() == displayName) {
-                displayId = increment.display_creation().id();
-                break;
-            }
-        }
-    }
-    return displayId;
-}
-
-class SurfaceInterceptorTest : public ::testing::Test {
-protected:
-    void SetUp() override {
-        // Allow SurfaceInterceptor write to /data
-        system("setenforce 0");
-
-        mComposerClient = sp<SurfaceComposerClient>::make();
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-    }
-
-    void TearDown() override {
-        mComposerClient->dispose();
-        mBGSurfaceControl.clear();
-        mFGSurfaceControl.clear();
-        mComposerClient.clear();
-        system("setenforce 1");
-    }
-
-    sp<SurfaceComposerClient> mComposerClient;
-    sp<SurfaceControl> mBGSurfaceControl;
-    sp<SurfaceControl> mFGSurfaceControl;
-    int32_t mBGLayerId;
-    int32_t mFGLayerId;
-
-public:
-    using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
-    using TestAction = void (SurfaceInterceptorTest::*)();
-    using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&);
-    using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&);
-
-    void setupBackgroundSurface();
-    void preProcessTrace(const Trace& trace);
-
-    // captureTest will enable SurfaceInterceptor, setup background surface,
-    // disable SurfaceInterceptor, collect the trace and process the trace for
-    // id of background surface before further verification.
-    void captureTest(TestTransactionAction action, TestBooleanVerification verification);
-    void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase);
-    void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase);
-    void captureTest(TestAction action, TestBooleanVerification verification);
-    void captureTest(TestAction action, TestVerification verification);
-    void runInTransaction(TestTransactionAction action);
-
-    // Verification of changes to a surface
-    bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
-    bool sizeUpdateFound(const SurfaceChange& change, bool foundSize);
-    bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
-    bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
-    bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
-    bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
-    bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
-                                         bool foundBackgroundBlurRadius);
-    bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions);
-    bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
-    bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
-    bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
-    bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack);
-    bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
-    bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
-    bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
-    bool reparentUpdateFound(const SurfaceChange& change, bool found);
-    bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
-    bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
-    bool trustedOverlayUpdateFound(const SurfaceChange& change, bool found);
-    bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
-
-    // Find all of the updates in the single trace
-    void assertAllUpdatesFound(const Trace& trace);
-
-    // Verification of creation and deletion of a surface
-    bool surfaceCreationFound(const Increment& increment, bool foundSurface);
-    bool surfaceDeletionFound(const Increment& increment, const int32_t targetId,
-            bool foundSurface);
-    bool displayCreationFound(const Increment& increment, bool foundDisplay);
-    bool displayDeletionFound(const Increment& increment, const int32_t targetId,
-            bool foundDisplay);
-    bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase);
-
-    // Verification of buffer updates
-    bool bufferUpdatesFound(const Trace& trace);
-
-    // Perform each of the possible changes to a surface
-    void positionUpdate(Transaction&);
-    void sizeUpdate(Transaction&);
-    void alphaUpdate(Transaction&);
-    void layerUpdate(Transaction&);
-    void cropUpdate(Transaction&);
-    void cornerRadiusUpdate(Transaction&);
-    void backgroundBlurRadiusUpdate(Transaction&);
-    void blurRegionsUpdate(Transaction&);
-    void matrixUpdate(Transaction&);
-    void transparentRegionHintUpdate(Transaction&);
-    void layerStackUpdate(Transaction&);
-    void hiddenFlagUpdate(Transaction&);
-    void opaqueFlagUpdate(Transaction&);
-    void secureFlagUpdate(Transaction&);
-    void reparentUpdate(Transaction&);
-    void relativeParentUpdate(Transaction&);
-    void shadowRadiusUpdate(Transaction&);
-    void trustedOverlayUpdate(Transaction&);
-    void surfaceCreation(Transaction&);
-    void displayCreation(Transaction&);
-    void displayDeletion(Transaction&);
-
-    void nBufferUpdates();
-    void runAllUpdates();
-
-private:
-    void captureInTransaction(TestTransactionAction action, Trace*);
-    void capture(TestAction action, Trace*);
-};
-
-void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) {
-    enableInterceptor();
-    setupBackgroundSurface();
-    runInTransaction(action);
-    disableInterceptor();
-    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
-    preProcessTrace(*outTrace);
-}
-
-void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) {
-    enableInterceptor();
-    setupBackgroundSurface();
-    (this->*action)();
-    disableInterceptor();
-    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
-    preProcessTrace(*outTrace);
-}
-
-void SurfaceInterceptorTest::setupBackgroundSurface() {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
-    ASSERT_FALSE(display == nullptr);
-
-    ui::DisplayMode mode;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-    const ui::Size& resolution = mode.resolution;
-
-    // Background surface
-    mBGSurfaceControl =
-            mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(),
-                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mBGSurfaceControl != nullptr);
-    ASSERT_TRUE(mBGSurfaceControl->isValid());
-
-    // Foreground surface
-    mFGSurfaceControl =
-            mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(),
-                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mFGSurfaceControl != nullptr);
-    ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-    Transaction t;
-    t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-    ASSERT_EQ(NO_ERROR,
-              t.setLayer(mBGSurfaceControl, INT_MAX - 3)
-                      .show(mBGSurfaceControl)
-                      .setLayer(mFGSurfaceControl, INT_MAX - 3)
-                      .show(mFGSurfaceControl)
-                      .apply());
-}
-
-void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
-    mBGLayerId = getSurfaceId(trace, TEST_BG_SURFACE_NAME);
-    mFGLayerId = getSurfaceId(trace, TEST_FG_SURFACE_NAME);
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        TestBooleanVerification verification) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE((this->*verification)(capturedTrace));
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        Increment::IncrementCase incrementCase) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase));
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        SurfaceChange::SurfaceChangeCase changeCase) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase));
-}
-
-void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) {
-    Trace capturedTrace;
-    capture(action, &capturedTrace);
-    ASSERT_TRUE((this->*verification)(capturedTrace));
-}
-
-void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) {
-    Trace capturedTrace;
-    capture(action, &capturedTrace);
-    (this->*verification)(capturedTrace);
-}
-
-void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) {
-    Transaction t;
-    (this->*action)(t);
-    t.apply(true);
-}
-
-void SurfaceInterceptorTest::positionUpdate(Transaction& t) {
-    t.setPosition(mBGSurfaceControl, POSITION_UPDATE, POSITION_UPDATE);
-}
-
-void SurfaceInterceptorTest::sizeUpdate(Transaction& t) {
-    t.setSize(mBGSurfaceControl, SIZE_UPDATE, SIZE_UPDATE);
-}
-
-void SurfaceInterceptorTest::alphaUpdate(Transaction& t) {
-    t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
-}
-
-void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
-    t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) {
-    t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) {
-    BLUR_REGIONS_UPDATE.empty();
-    BLUR_REGIONS_UPDATE.push_back(BlurRegion());
-    t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE);
-}
-
-void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
-    t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
-}
-
-void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
-    t.setCrop(mBGSurfaceControl, CROP_UPDATE);
-}
-
-void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
-    t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
-}
-
-void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) {
-    Region region(CROP_UPDATE);
-    t.setTransparentRegionHint(mBGSurfaceControl, region);
-}
-
-void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) {
-    t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE));
-}
-
-void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
-}
-
-void SurfaceInterceptorTest::opaqueFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
-}
-
-void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
-}
-
-void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
-    t.reparent(mBGSurfaceControl, mFGSurfaceControl);
-}
-
-void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
-    t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
-}
-
-void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
-    t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::trustedOverlayUpdate(Transaction& t) {
-    t.setTrustedOverlay(mBGSurfaceControl, true);
-}
-
-void SurfaceInterceptorTest::displayCreation(Transaction&) {
-    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
-    SurfaceComposerClient::destroyDisplay(testDisplay);
-}
-
-void SurfaceInterceptorTest::displayDeletion(Transaction&) {
-    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
-    SurfaceComposerClient::destroyDisplay(testDisplay);
-}
-
-void SurfaceInterceptorTest::runAllUpdates() {
-    runInTransaction(&SurfaceInterceptorTest::positionUpdate);
-    runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
-    runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
-    runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate);
-    runInTransaction(&SurfaceInterceptorTest::layerUpdate);
-    runInTransaction(&SurfaceInterceptorTest::cropUpdate);
-    runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
-    runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
-    runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
-    runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
-    runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
-    runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::trustedOverlayUpdate);
-}
-
-void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
-    mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
-            PIXEL_FORMAT_RGBA_8888, 0);
-}
-
-void SurfaceInterceptorTest::nBufferUpdates() {
-    std::random_device rd;
-    std::mt19937_64 gen(rd());
-    // This makes testing fun
-    std::uniform_int_distribution<uint8_t> dis;
-    for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
-        fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
-    }
-}
-
-bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) {
-    // There should only be one position transaction with x and y = POSITION_UPDATE
-    bool hasX(change.position().x() == POSITION_UPDATE);
-    bool hasY(change.position().y() == POSITION_UPDATE);
-    if (hasX && hasY && !foundPosition) {
-        foundPosition = true;
-    } else if (hasX && hasY && foundPosition) {
-        // Failed because the position update was found a second time
-        [] () { FAIL(); }();
-    }
-    return foundPosition;
-}
-
-bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool foundSize) {
-    bool hasWidth(change.size().h() == SIZE_UPDATE);
-    bool hasHeight(change.size().w() == SIZE_UPDATE);
-    if (hasWidth && hasHeight && !foundSize) {
-        foundSize = true;
-    } else if (hasWidth && hasHeight && foundSize) {
-        [] () { FAIL(); }();
-    }
-    return foundSize;
-}
-
-bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) {
-    bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
-    if (hasAlpha && !foundAlpha) {
-        foundAlpha = true;
-    } else if (hasAlpha && foundAlpha) {
-        [] () { FAIL(); }();
-    }
-    return foundAlpha;
-}
-
-bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
-                                                     bool foundCornerRadius) {
-    bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
-    if (hasCornerRadius && !foundCornerRadius) {
-        foundCornerRadius = true;
-    } else if (hasCornerRadius && foundCornerRadius) {
-        [] () { FAIL(); }();
-    }
-    return foundCornerRadius;
-}
-
-bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
-                                                             bool foundBackgroundBlur) {
-    bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() ==
-                           BACKGROUND_BLUR_RADIUS_UPDATE);
-    if (hasBackgroundBlur && !foundBackgroundBlur) {
-        foundBackgroundBlur = true;
-    } else if (hasBackgroundBlur && foundBackgroundBlur) {
-        []() { FAIL(); }();
-    }
-    return foundBackgroundBlur;
-}
-
-bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change,
-                                                    bool foundBlurRegions) {
-    bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size());
-    if (hasBlurRegions && !foundBlurRegions) {
-        foundBlurRegions = true;
-    } else if (hasBlurRegions && foundBlurRegions) {
-        []() { FAIL(); }();
-    }
-    return foundBlurRegions;
-}
-
-bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
-    bool hasLayer(change.layer().layer() == LAYER_UPDATE);
-    if (hasLayer && !foundLayer) {
-        foundLayer = true;
-    } else if (hasLayer && foundLayer) {
-        [] () { FAIL(); }();
-    }
-    return foundLayer;
-}
-
-bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) {
-    bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
-    bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
-    bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
-    bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
-    if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
-        foundCrop = true;
-    } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
-        [] () { FAIL(); }();
-    }
-    return foundCrop;
-}
-
-bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
-    bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
-    bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
-    bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2);
-    bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2);
-    if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
-        foundMatrix = true;
-    } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
-        [] () { FAIL(); }();
-    }
-    return foundMatrix;
-}
-
-bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
-        bool foundTransparentRegion) {
-    auto traceRegion = change.transparent_region_hint().region(0);
-    bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
-    bool hasTop(traceRegion.top() == CROP_UPDATE.top);
-    bool hasRight(traceRegion.right() == CROP_UPDATE.right);
-    bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
-    if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
-        foundTransparentRegion = true;
-    } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
-        [] () { FAIL(); }();
-    }
-    return foundTransparentRegion;
-}
-
-bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
-        bool foundLayerStack) {
-    bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
-    if (hasLayerStackUpdate && !foundLayerStack) {
-        foundLayerStack = true;
-    } else if (hasLayerStackUpdate && foundLayerStack) {
-        [] () { FAIL(); }();
-    }
-    return foundLayerStack;
-}
-
-bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
-        bool foundHiddenFlag) {
-    bool hasHiddenFlag(change.hidden_flag().hidden_flag());
-    if (hasHiddenFlag && !foundHiddenFlag) {
-        foundHiddenFlag = true;
-    } else if (hasHiddenFlag && foundHiddenFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundHiddenFlag;
-}
-
-bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
-        bool foundOpaqueFlag) {
-    bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
-    if (hasOpaqueFlag && !foundOpaqueFlag) {
-        foundOpaqueFlag = true;
-    } else if (hasOpaqueFlag && foundOpaqueFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundOpaqueFlag;
-}
-
-bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
-        bool foundSecureFlag) {
-    bool hasSecureFlag(change.secure_flag().secure_flag());
-    if (hasSecureFlag && !foundSecureFlag) {
-        foundSecureFlag = true;
-    } else if (hasSecureFlag && foundSecureFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundSecureFlag;
-}
-
-bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) {
-    bool hasId(change.reparent().parent_id() == mFGLayerId);
-    if (hasId && !found) {
-        found = true;
-    } else if (hasId && found) {
-        []() { FAIL(); }();
-    }
-    return found;
-}
-
-bool SurfaceInterceptorTest::relativeParentUpdateFound(const SurfaceChange& change, bool found) {
-    bool hasId(change.relative_parent().relative_parent_id() == mFGLayerId);
-    if (hasId && !found) {
-        found = true;
-    } else if (hasId && found) {
-        []() { FAIL(); }();
-    }
-    return found;
-}
-
-bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
-                                                     bool foundShadowRadius) {
-    bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
-    if (hasShadowRadius && !foundShadowRadius) {
-        foundShadowRadius = true;
-    } else if (hasShadowRadius && foundShadowRadius) {
-        []() { FAIL(); }();
-    }
-    return foundShadowRadius;
-}
-
-bool SurfaceInterceptorTest::trustedOverlayUpdateFound(const SurfaceChange& change,
-                                                       bool foundTrustedOverlay) {
-    bool hasTrustedOverlay(change.trusted_overlay().is_trusted_overlay());
-    if (hasTrustedOverlay && !foundTrustedOverlay) {
-        foundTrustedOverlay = true;
-    } else if (hasTrustedOverlay && foundTrustedOverlay) {
-        []() { FAIL(); }();
-    }
-    return foundTrustedOverlay;
-}
-
-bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
-        SurfaceChange::SurfaceChangeCase changeCase) {
-    bool foundUpdate = false;
-    for (const auto& increment : trace.increment()) {
-        if (increment.increment_case() == increment.kTransaction) {
-            for (const auto& change : increment.transaction().surface_change()) {
-                if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
-                    switch (changeCase) {
-                        case SurfaceChange::SurfaceChangeCase::kPosition:
-                            // foundUpdate is sent for the tests to fail on duplicated increments
-                            foundUpdate = positionUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kSize:
-                            foundUpdate = sizeUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kAlpha:
-                            foundUpdate = alphaUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kLayer:
-                            foundUpdate = layerUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kCrop:
-                            foundUpdate = cropUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kCornerRadius:
-                            foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
-                            foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kBlurRegions:
-                            foundUpdate = blurRegionsUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kMatrix:
-                            foundUpdate = matrixUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
-                            foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kLayerStack:
-                            foundUpdate = layerStackUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
-                            foundUpdate = hiddenFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
-                            foundUpdate = opaqueFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kSecureFlag:
-                            foundUpdate = secureFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kReparent:
-                            foundUpdate = reparentUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kRelativeParent:
-                            foundUpdate = relativeParentUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kShadowRadius:
-                            foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kTrustedOverlay:
-                            foundUpdate = trustedOverlayUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
-                            break;
-                    }
-                }
-            }
-        }
-    }
-    return foundUpdate;
-}
-
-void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) {
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent));
-}
-
-bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
-    bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment));
-    if (isMatch && !foundSurface) {
-        foundSurface = true;
-    } else if (isMatch && foundSurface) {
-        [] () { FAIL(); }();
-    }
-    return foundSurface;
-}
-
-bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment,
-        const int32_t targetId, bool foundSurface) {
-    bool isMatch(increment.surface_deletion().id() == targetId);
-    if (isMatch && !foundSurface) {
-        foundSurface = true;
-    } else if (isMatch && foundSurface) {
-        [] () { FAIL(); }();
-    }
-    return foundSurface;
-}
-
-bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
-    bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
-                 !increment.display_creation().is_secure());
-    if (isMatch && !foundDisplay) {
-        foundDisplay = true;
-    } else if (isMatch && foundDisplay) {
-        [] () { FAIL(); }();
-    }
-    return foundDisplay;
-}
-
-bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment,
-        const int32_t targetId, bool foundDisplay) {
-    bool isMatch(increment.display_deletion().id() == targetId);
-    if (isMatch && !foundDisplay) {
-        foundDisplay = true;
-    } else if (isMatch && foundDisplay) {
-        [] () { FAIL(); }();
-    }
-    return foundDisplay;
-}
-
-bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace,
-        Increment::IncrementCase incrementCase) {
-    bool foundIncrement = false;
-    for (const auto& increment : trace.increment()) {
-        if (increment.increment_case() == incrementCase) {
-            int32_t targetId = 0;
-            switch (incrementCase) {
-                case Increment::IncrementCase::kSurfaceCreation:
-                    foundIncrement = surfaceCreationFound(increment, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kSurfaceDeletion:
-                    // Find the id of created surface.
-                    targetId = getSurfaceId(trace, LAYER_NAME);
-                    foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kDisplayCreation:
-                    foundIncrement = displayCreationFound(increment, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kDisplayDeletion:
-                    // Find the id of created display.
-                    targetId = getDisplayId(trace, DISPLAY_NAME.string());
-                    foundIncrement = displayDeletionFound(increment, targetId, foundIncrement);
-                    break;
-                default:
-                    /* code */
-                    break;
-            }
-        }
-    }
-    return foundIncrement;
-}
-
-bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) {
-    uint32_t updates = 0;
-    for (const auto& inc : trace.increment()) {
-        if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
-            updates++;
-        }
-    }
-    return updates == BUFFER_UPDATES;
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::positionUpdate,
-            SurfaceChange::SurfaceChangeCase::kPosition);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
-            SurfaceChange::SurfaceChangeCase::kCornerRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate,
-                SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::blurRegionsUpdate,
-                SurfaceChange::SurfaceChangeCase::kBlurRegions);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
-            SurfaceChange::SurfaceChangeCase::kTransparentRegionHint);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::layerStackUpdate,
-            SurfaceChange::SurfaceChangeCase::kLayerStack);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kHiddenFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kOpaqueFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::secureFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kSecureFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::reparentUpdate,
-                SurfaceChange::SurfaceChangeCase::kReparent);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::relativeParentUpdate,
-                SurfaceChange::SurfaceChangeCase::kRelativeParent);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
-                SurfaceChange::SurfaceChangeCase::kShadowRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptTrustedOverlayUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::trustedOverlayUpdate,
-                SurfaceChange::SurfaceChangeCase::kTrustedOverlay);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
-    captureTest(&SurfaceInterceptorTest::runAllUpdates,
-                &SurfaceInterceptorTest::assertAllUpdatesFound);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
-    captureTest(&SurfaceInterceptorTest::surfaceCreation,
-            Increment::IncrementCase::kSurfaceCreation);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
-    captureTest(&SurfaceInterceptorTest::displayCreation,
-            Increment::IncrementCase::kDisplayCreation);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
-    enableInterceptor();
-    runInTransaction(&SurfaceInterceptorTest::displayDeletion);
-    disableInterceptor();
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
-}
-
-// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
-// first create a snapshot of the existing displays and surfaces and then start capturing
-// the buffer updates
-TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
-    setupBackgroundSurface();
-    std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
-    enableInterceptor();
-    disableInterceptor();
-    bufferUpdates.join();
-
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    const auto& firstIncrement = capturedTrace.mutable_increment(0);
-    ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
-}
-}
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index ad03ed3..797a64c 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -35,7 +35,10 @@
                 return mDelegate->screenshot();
             case RenderPath::VIRTUAL_DISPLAY:
 
-                const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+                const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+                const auto displayToken = ids.empty()
+                        ? nullptr
+                        : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
 
                 ui::DisplayState displayState;
                 SurfaceComposerClient::getDisplayState(displayToken, &displayState);
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
deleted file mode 100644
index 704815d..0000000
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ /dev/null
@@ -1,63 +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"],
-}
-
-cc_test {
-    name: "sffakehwc_test",
-    defaults: ["surfaceflinger_defaults"],
-    test_suites: ["device-tests"],
-    srcs: [
-        "FakeComposerClient.cpp",
-        "FakeComposerService.cpp",
-        "FakeComposerUtils.cpp",
-        "SFFakeHwc_test.cpp",
-    ],
-    require_root: true,
-    shared_libs: [
-        "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.graphics.mapper@2.0",
-        "android.hardware.graphics.mapper@3.0",
-        "android.hardware.graphics.mapper@4.0",
-        "android.hardware.power@1.3",
-        "android.hardware.power-V2-cpp",
-        "libbase",
-        "libbinder",
-        "libbinder_ndk",
-        "libcutils",
-        "libfmq",
-        "libgui",
-        "libhidlbase",
-        "liblayers_proto",
-        "liblog",
-        "libnativewindow",
-        "libsync",
-        "libtimestats",
-        "libui",
-        "libutils",
-    ],
-    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/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
deleted file mode 100644
index a5cca35..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ /dev/null
@@ -1,929 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeComposer"
-
-#include "FakeComposerClient.h"
-
-#include <gui/SurfaceComposerClient.h>
-
-#include <log/log.h>
-
-#include <gtest/gtest.h>
-
-#include <inttypes.h>
-#include <time.h>
-#include <algorithm>
-#include <condition_variable>
-#include <iostream>
-#include <mutex>
-#include <set>
-#include <thread>
-
-constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0);
-
-using namespace sftest;
-
-using android::Condition;
-using android::Mutex;
-
-using Clock = std::chrono::steady_clock;
-using TimePoint = std::chrono::time_point<Clock>;
-
-namespace {
-
-// Internal state of a layer in the HWC API.
-class LayerImpl {
-public:
-    LayerImpl() = default;
-
-    bool mValid = true;
-    RenderState mRenderState;
-    uint32_t mZ = 0;
-};
-
-// Struct for storing per frame rectangle state. Contains the render
-// state shared to the test case. Basically a snapshot and a subset of
-// LayerImpl sufficient to re-create the pixels of a layer for the
-// frame.
-struct FrameRect {
-public:
-    FrameRect(Layer layer_, const RenderState& state, uint32_t z_)
-          : layer(layer_), renderState(state), z(z_) {}
-
-    const Layer layer;
-    const RenderState renderState;
-    const uint32_t z;
-};
-
-// Collection of FrameRects forming one rendered frame. Could store
-// related fences and other data in the future.
-class Frame {
-public:
-    Frame() = default;
-    std::vector<std::unique_ptr<FrameRect>> rectangles;
-};
-
-class DelayedEventGenerator {
-public:
-    explicit DelayedEventGenerator(std::function<void()> onTimerExpired)
-          : mOnTimerExpired(onTimerExpired) {
-        mThread = std::thread([this]() { loop(); });
-    }
-
-    ~DelayedEventGenerator() {
-        ALOGI("DelayedEventGenerator exiting.");
-        {
-            std::unique_lock<std::mutex> lock(mMutex);
-            mRunning = false;
-            mWakeups.clear();
-            mCondition.notify_one();
-        }
-        mThread.join();
-        ALOGI("DelayedEventGenerator exited.");
-    }
-
-    void wakeAfter(std::chrono::nanoseconds waitTime) {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mWakeups.insert(Clock::now() + waitTime);
-        mCondition.notify_one();
-    }
-
-private:
-    void loop() {
-        while (true) {
-            // Lock scope
-            {
-                std::unique_lock<std::mutex> lock(mMutex);
-                mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); });
-                if (!mRunning && mWakeups.empty()) {
-                    // This thread should only exit once the destructor has been called and all
-                    // wakeups have been processed
-                    return;
-                }
-
-                // At this point, mWakeups will not be empty
-
-                TimePoint target = *(mWakeups.begin());
-                auto status = mCondition.wait_until(lock, target);
-                while (status == std::cv_status::no_timeout) {
-                    // This was either a spurious wakeup or another wakeup was added, so grab the
-                    // oldest point and wait again
-                    target = *(mWakeups.begin());
-                    status = mCondition.wait_until(lock, target);
-                }
-
-                // status must have been timeout, so we can finally clear this point
-                mWakeups.erase(target);
-            }
-            // Callback *without* locks!
-            mOnTimerExpired();
-        }
-    }
-
-    std::function<void()> mOnTimerExpired;
-    std::thread mThread;
-    std::mutex mMutex;
-    std::condition_variable mCondition;
-    bool mRunning = true;
-    std::set<TimePoint> mWakeups;
-};
-
-} // namespace
-
-FakeComposerClient::FakeComposerClient()
-      : mEventCallback(nullptr),
-        mEventCallback_2_4(nullptr),
-        mCurrentConfig(NULL_DISPLAY_CONFIG),
-        mVsyncEnabled(false),
-        mLayers(),
-        mDelayedEventGenerator(
-                std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })),
-        mSurfaceComposer(nullptr) {}
-
-FakeComposerClient::~FakeComposerClient() {}
-
-bool FakeComposerClient::hasCapability(hwc2_capability_t /*capability*/) {
-    return false;
-}
-
-std::string FakeComposerClient::dumpDebugInfo() {
-    return {};
-}
-
-void FakeComposerClient::registerEventCallback(EventCallback* callback) {
-    ALOGV("registerEventCallback");
-    LOG_FATAL_IF(mEventCallback_2_4 != nullptr,
-                 "already registered using registerEventCallback_2_4");
-
-    mEventCallback = callback;
-    if (mEventCallback) {
-        mEventCallback->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
-    }
-}
-
-void FakeComposerClient::unregisterEventCallback() {
-    ALOGV("unregisterEventCallback");
-    mEventCallback = nullptr;
-}
-
-void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
-    if (mEventCallback) {
-        mEventCallback->onHotplug(display, state);
-    } else if (mEventCallback_2_4) {
-        mEventCallback_2_4->onHotplug(display, state);
-    }
-}
-
-void FakeComposerClient::refreshDisplay(Display display) {
-    if (mEventCallback) {
-        mEventCallback->onRefresh(display);
-    } else if (mEventCallback_2_4) {
-        mEventCallback_2_4->onRefresh(display);
-    }
-}
-
-uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
-    ALOGV("getMaxVirtualDisplayCount");
-    return 1;
-}
-
-V2_1::Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
-                                                     V1_0::PixelFormat* /*format*/,
-                                                     Display* /*outDisplay*/) {
-    ALOGV("createVirtualDisplay");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
-    ALOGV("destroyVirtualDisplay");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
-    ALOGV("createLayer");
-    *outLayer = mLayers.size();
-    auto newLayer = std::make_unique<LayerImpl>();
-    mLayers.push_back(std::move(newLayer));
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
-    ALOGV("destroyLayer");
-    mLayers[layer]->mValid = false;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getActiveConfig(Display display, Config* outConfig) {
-    ALOGV("getActiveConfig");
-    if (mMockHal) {
-        return mMockHal->getActiveConfig(display, outConfig);
-    }
-
-    // TODO Assert outConfig != nullptr
-
-    // TODO This is my reading of the
-    // IComposerClient::getActiveConfig, but returning BAD_CONFIG
-    // seems to not fit SurfaceFlinger plans. See version 2 below.
-    // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
-    //     return V2_1::Error::BAD_CONFIG;
-    // }
-    //*outConfig = mCurrentConfig;
-    *outConfig = 1; // Very special config for you my friend
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
-                                                       uint32_t /*height*/,
-                                                       V1_0::PixelFormat /*format*/,
-                                                       V1_0::Dataspace /*dataspace*/) {
-    ALOGV("getClientTargetSupport");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getColorModes(Display /*display*/,
-                                              hidl_vec<V1_0::ColorMode>* /*outModes*/) {
-    ALOGV("getColorModes");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
-                                                    V2_1::IComposerClient::Attribute attribute,
-                                                    int32_t* outValue) {
-    auto tmpError =
-            getDisplayAttribute_2_4(display, config,
-                                    static_cast<IComposerClient::Attribute>(attribute), outValue);
-    return static_cast<V2_1::Error>(tmpError);
-}
-
-V2_1::Error FakeComposerClient::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
-    ALOGV("getDisplayConfigs");
-    if (mMockHal) {
-        return mMockHal->getDisplayConfigs(display, outConfigs);
-    }
-
-    // TODO assert display == 1, outConfigs != nullptr
-
-    outConfigs->resize(1);
-    (*outConfigs)[0] = 1;
-
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
-    ALOGV("getDisplayName");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayType(Display /*display*/,
-                                               IComposerClient::DisplayType* outType) {
-    ALOGV("getDisplayType");
-    // TODO: This setting nothing on the output had no effect on initial trials. Is first display
-    // assumed to be physical?
-    *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
-    ALOGV("getDozeSupport");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getHdrCapabilities(Display /*display*/,
-                                                   hidl_vec<V1_0::Hdr>* /*outTypes*/,
-                                                   float* /*outMaxLuminance*/,
-                                                   float* /*outMaxAverageLuminance*/,
-                                                   float* /*outMinLuminance*/) {
-    ALOGV("getHdrCapabilities");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setActiveConfig(Display display, Config config) {
-    ALOGV("setActiveConfig");
-    if (mMockHal) {
-        return mMockHal->setActiveConfig(display, config);
-    }
-    mCurrentConfig = config;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setColorMode(Display /*display*/, V1_0::ColorMode /*mode*/) {
-    ALOGV("setColorMode");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setPowerMode(Display /*display*/,
-                                             V2_1::IComposerClient::PowerMode /*mode*/) {
-    ALOGV("setPowerMode");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setVsyncEnabled(Display /*display*/,
-                                                IComposerClient::Vsync enabled) {
-    mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
-    ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
-                                                  int32_t /*hint*/) {
-    ALOGV("setColorTransform");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
-                                                int32_t /*acquireFence*/, int32_t /*dataspace*/,
-                                                const std::vector<hwc_rect_t>& /*damage*/) {
-    ALOGV("setClientTarget");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
-                                                int32_t /*releaseFence*/) {
-    ALOGV("setOutputBuffer");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::validateDisplay(
-        Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
-        std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
-        uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
-        std::vector<uint32_t>* /*outRequestMasks*/) {
-    ALOGV("validateDisplay");
-    // TODO: Assume touching nothing means All Korrekt!
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
-    ALOGV("acceptDisplayChanges");
-    // Didn't ask for changes because software is omnipotent.
-    return V2_1::Error::NONE;
-}
-
-bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
-    return a->z <= b->z;
-}
-
-V2_1::Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
-                                               std::vector<Layer>* /*outLayers*/,
-                                               std::vector<int32_t>* /*outReleaseFences*/) {
-    ALOGV("presentDisplay");
-    // TODO Leaving layers and their fences out for now. Doing so
-    // means that we've already processed everything. Important to
-    // test that the fences are respected, though. (How?)
-
-    std::unique_ptr<Frame> newFrame(new Frame);
-    for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
-        const LayerImpl& layerImpl = *mLayers[layer];
-
-        if (!layerImpl.mValid) continue;
-
-        auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
-        newFrame->rectangles.push_back(std::move(rect));
-    }
-    std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
-    {
-        Mutex::Autolock _l(mStateMutex);
-        mFrames.push_back(std::move(newFrame));
-        mFramesAvailable.broadcast();
-    }
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
-                                                       int32_t /*x*/, int32_t /*y*/) {
-    ALOGV("setLayerCursorPosition");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer,
-                                               buffer_handle_t buffer, int32_t acquireFence) {
-    ALOGV("setLayerBuffer");
-    LayerImpl& l = getLayerImpl(layer);
-    if (buffer != l.mRenderState.mBuffer) {
-        l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
-    }
-    l.mRenderState.mBuffer = buffer;
-    l.mRenderState.mAcquireFence = acquireFence;
-
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
-                                                      const std::vector<hwc_rect_t>& /*damage*/) {
-    ALOGV("setLayerSurfaceDamage");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
-    ALOGV("setLayerBlendMode");
-    getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
-                                              IComposerClient::Color color) {
-    ALOGV("setLayerColor");
-    getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
-    getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
-    getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
-    getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
-                                                        int32_t /*type*/) {
-    ALOGV("setLayerCompositionType");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
-                                                  int32_t /*dataspace*/) {
-    ALOGV("setLayerDataspace");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
-                                                     const hwc_rect_t& frame) {
-    ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
-          frame.bottom);
-    getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
-    ALOGV("setLayerPlaneAlpha");
-    getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
-                                                       buffer_handle_t /*stream*/) {
-    ALOGV("setLayerSidebandStream");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
-                                                   const hwc_frect_t& crop) {
-    ALOGV("setLayerSourceCrop");
-    getLayerImpl(layer).mRenderState.mSourceCrop = crop;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer,
-                                                  int32_t transform) {
-    ALOGV("setLayerTransform");
-    getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
-                                                      const std::vector<hwc_rect_t>& visible) {
-    ALOGV("setLayerVisibleRegion");
-    getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
-    ALOGV("setLayerZOrder");
-    getLayerImpl(layer).mZ = z;
-    return V2_1::Error::NONE;
-}
-
-// Composer 2.2
-V2_1::Error FakeComposerClient::getPerFrameMetadataKeys(
-        Display /*display*/, std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadata(
-        Display /*display*/, Layer /*layer*/,
-        const std::vector<V2_2::IComposerClient::PerFrameMetadata>& /*metadata*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferAttributes(
-        Display /*display*/, graphics::common::V1_1::PixelFormat* /*outFormat*/,
-        graphics::common::V1_1::Dataspace* /*outDataspace*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setReadbackBuffer(Display /*display*/,
-                                                  const native_handle_t* /*bufferHandle*/,
-                                                  android::base::unique_fd /*fenceFd*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferFence(Display /*display*/,
-                                                       android::base::unique_fd* /*outFenceFd*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::createVirtualDisplay_2_2(
-        uint32_t /*width*/, uint32_t /*height*/, graphics::common::V1_1::PixelFormat* /*format*/,
-        Display* /*outDisplay*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-V2_1::Error FakeComposerClient::getClientTargetSupport_2_2(
-        Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
-        graphics::common::V1_1::PixelFormat /*format*/,
-        graphics::common::V1_1::Dataspace /*dataspace*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setPowerMode_2_2(Display /*display*/,
-                                                 V2_2::IComposerClient::PowerMode /*mode*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerFloatColor(Display /*display*/, Layer /*layer*/,
-                                                   V2_2::IComposerClient::FloatColor /*color*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getColorModes_2_2(
-        Display /*display*/, hidl_vec<graphics::common::V1_1::ColorMode>* /*outModes*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getRenderIntents(
-        Display /*display*/, graphics::common::V1_1::ColorMode /*mode*/,
-        std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setColorMode_2_2(Display /*display*/,
-                                                 graphics::common::V1_1::ColorMode /*mode*/,
-                                                 graphics::common::V1_1::RenderIntent /*intent*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-std::array<float, 16> FakeComposerClient::getDataspaceSaturationMatrix(
-        graphics::common::V1_1::Dataspace /*dataspace*/) {
-    return {};
-}
-
-// Composer 2.3
-V2_1::Error FakeComposerClient::getPerFrameMetadataKeys_2_3(
-        Display /*display*/, std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setColorMode_2_3(Display /*display*/,
-                                                 graphics::common::V1_2::ColorMode /*mode*/,
-                                                 graphics::common::V1_1::RenderIntent /*intent*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getRenderIntents_2_3(
-        Display /*display*/, graphics::common::V1_2::ColorMode /*mode*/,
-        std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getColorModes_2_3(
-        Display /*display*/, hidl_vec<graphics::common::V1_2::ColorMode>* /*outModes*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getClientTargetSupport_2_3(
-        Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
-        graphics::common::V1_2::PixelFormat /*format*/,
-        graphics::common::V1_2::Dataspace /*dataspace*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferAttributes_2_3(
-        Display /*display*/, graphics::common::V1_2::PixelFormat* /*outFormat*/,
-        graphics::common::V1_2::Dataspace* /*outDataspace*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getHdrCapabilities_2_3(
-        Display /*display*/, hidl_vec<graphics::common::V1_2::Hdr>* /*outTypes*/,
-        float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadata_2_3(
-        Display /*display*/, Layer /*layer*/,
-        const std::vector<V2_3::IComposerClient::PerFrameMetadata>& /*metadata*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayIdentificationData(Display /*display*/,
-                                                             uint8_t* /*outPort*/,
-                                                             std::vector<uint8_t>* /*outData*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerColorTransform(Display /*display*/, Layer /*layer*/,
-                                                       const float* /*matrix*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayedContentSamplingAttributes(
-        uint64_t /*display*/, graphics::common::V1_2::PixelFormat& /*format*/,
-        graphics::common::V1_2::Dataspace& /*dataspace*/,
-        hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& /*componentMask*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setDisplayedContentSamplingEnabled(
-        uint64_t /*display*/, V2_3::IComposerClient::DisplayedContentSampling /*enable*/,
-        hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> /*componentMask*/,
-        uint64_t /*maxFrames*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayedContentSample(
-        uint64_t /*display*/, uint64_t /*maxFrames*/, uint64_t /*timestamp*/,
-        uint64_t& /*frameCount*/, hidl_vec<uint64_t>& /*sampleComponent0*/,
-        hidl_vec<uint64_t>& /*sampleComponent1*/, hidl_vec<uint64_t>& /*sampleComponent2*/,
-        hidl_vec<uint64_t>& /*sampleComponent3*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayCapabilities(
-        Display /*display*/,
-        std::vector<V2_3::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadataBlobs(
-        Display /*display*/, Layer /*layer*/,
-        std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& /*blobs*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayBrightnessSupport(Display /*display*/,
-                                                            bool* /*outSupport*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setDisplayBrightness(Display /*display*/, float /*brightness*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-// Composer 2.4
-void FakeComposerClient::registerEventCallback_2_4(EventCallback_2_4* callback) {
-    ALOGV("registerEventCallback_2_4");
-    LOG_FATAL_IF(mEventCallback != nullptr, "already registered using registerEventCallback");
-
-    mEventCallback_2_4 = callback;
-    if (mEventCallback_2_4) {
-        mEventCallback_2_4->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
-    }
-}
-
-void FakeComposerClient::unregisterEventCallback_2_4() {
-    ALOGV("unregisterEventCallback_2_4");
-    mEventCallback_2_4 = nullptr;
-}
-
-V2_4::Error FakeComposerClient::getDisplayCapabilities_2_4(
-        Display /*display*/,
-        std::vector<V2_4::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getDisplayConnectionType(
-        Display /*display*/, V2_4::IComposerClient::DisplayConnectionType* /*outType*/) {
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getDisplayAttribute_2_4(Display display, Config config,
-                                                        IComposerClient::Attribute attribute,
-                                                        int32_t* outValue) {
-    ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
-          static_cast<int>(config), static_cast<int>(attribute), outValue);
-    if (mMockHal) {
-        return mMockHal->getDisplayAttribute_2_4(display, config, attribute, outValue);
-    }
-
-    // TODO: SOOO much fun to be had with these alone
-    switch (attribute) {
-        case IComposerClient::Attribute::WIDTH:
-            *outValue = 1920;
-            break;
-        case IComposerClient::Attribute::HEIGHT:
-            *outValue = 1080;
-            break;
-        case IComposerClient::Attribute::VSYNC_PERIOD:
-            *outValue = 1666666666;
-            break; // TOOD: Tests break down if lowered to 16ms?
-        case IComposerClient::Attribute::DPI_X:
-            *outValue = 240;
-            break;
-        case IComposerClient::Attribute::DPI_Y:
-            *outValue = 240;
-            break;
-        default:
-            LOG_ALWAYS_FATAL("Say what!?! New attribute");
-    }
-
-    return Error::NONE;
-}
-
-V2_4::Error FakeComposerClient::getDisplayVsyncPeriod(Display display,
-                                                      V2_4::VsyncPeriodNanos* outVsyncPeriod) {
-    ALOGV("getDisplayVsyncPeriod");
-    if (mMockHal) {
-        return mMockHal->getDisplayVsyncPeriod(display, outVsyncPeriod);
-    }
-
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setActiveConfigWithConstraints(
-        Display display, Config config,
-        const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
-        VsyncPeriodChangeTimeline* timeline) {
-    ALOGV("setActiveConfigWithConstraints");
-    if (mMockHal) {
-        return mMockHal->setActiveConfigWithConstraints(display, config,
-                                                        vsyncPeriodChangeConstraints, timeline);
-    }
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
-    ALOGV("setAutoLowLatencyMode");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getSupportedContentTypes(
-        Display, std::vector<IComposerClient::ContentType>*) {
-    ALOGV("getSupportedContentTypes");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
-    ALOGV("setContentType");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::validateDisplay_2_4(
-        Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
-        std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
-        uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
-        std::vector<uint32_t>* /*outRequestMasks*/,
-        IComposerClient::ClientTargetProperty* /*outClientTargetProperty*/) {
-    return V2_4::Error::NONE;
-}
-
-V2_4::Error FakeComposerClient::setLayerGenericMetadata(Display, Layer, const std::string&, bool,
-                                                        const std::vector<uint8_t>&) {
-    ALOGV("setLayerGenericMetadata");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getLayerGenericMetadataKeys(
-        std::vector<IComposerClient::LayerGenericMetadataKey>*) {
-    ALOGV("getLayerGenericMetadataKeys");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-//////////////////////////////////////////////////////////////////
-
-void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
-    if (mEventCallback || mEventCallback_2_4) {
-        uint64_t timestamp = vsyncTime;
-        ALOGV("Vsync");
-        if (timestamp == 0) {
-            struct timespec ts;
-            clock_gettime(CLOCK_MONOTONIC, &ts);
-            timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
-        }
-        if (mSurfaceComposer != nullptr) {
-            mSurfaceComposer->injectVSync(timestamp);
-        } else if (mEventCallback) {
-            mEventCallback->onVsync(PRIMARY_DISPLAY, timestamp);
-        } else {
-            mEventCallback_2_4->onVsync_2_4(PRIMARY_DISPLAY, timestamp, 16'666'666);
-        }
-    }
-}
-
-void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) {
-    mDelayedEventGenerator->wakeAfter(wait);
-}
-
-LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) {
-    // TODO Change these to an internal state check that can be
-    // invoked from the gtest? GTest macros do not seem all that safe
-    // when used outside the test class
-    EXPECT_GE(handle, static_cast<Layer>(0));
-    EXPECT_LT(handle, mLayers.size());
-    return *(mLayers[handle]);
-}
-
-int FakeComposerClient::getFrameCount() const {
-    return mFrames.size();
-}
-
-static std::vector<RenderState> extractRenderState(
-        const std::vector<std::unique_ptr<FrameRect>>& internalRects) {
-    std::vector<RenderState> result;
-    result.reserve(internalRects.size());
-    for (const std::unique_ptr<FrameRect>& rect : internalRects) {
-        result.push_back(rect->renderState);
-    }
-    return result;
-}
-
-std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const {
-    Mutex::Autolock _l(mStateMutex);
-    return extractRenderState(mFrames[frame]->rectangles);
-}
-
-std::vector<RenderState> FakeComposerClient::getLatestFrame() const {
-    Mutex::Autolock _l(mStateMutex);
-    return extractRenderState(mFrames[mFrames.size() - 1]->rectangles);
-}
-
-void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) {
-    int currentFrame = 0;
-    {
-        Mutex::Autolock _l(mStateMutex); // I hope this is ok...
-        currentFrame = static_cast<int>(mFrames.size());
-        requestVSync();
-    }
-    waitUntilFrame(currentFrame + 1, maxWait);
-}
-
-void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const {
-    Mutex::Autolock _l(mStateMutex);
-    while (mFrames.size() < static_cast<size_t>(targetFrame)) {
-        android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count());
-        if (result == android::TIMED_OUT) {
-            ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame,
-                  mFrames.size(), maxWait.count());
-            return;
-        }
-    }
-}
-
-void FakeComposerClient::clearFrames() {
-    Mutex::Autolock _l(mStateMutex);
-    mFrames.clear();
-    for (const std::unique_ptr<LayerImpl>& layer : mLayers) {
-        if (layer->mValid) {
-            layer->mRenderState.mSwapCount = 0;
-        }
-    }
-}
-
-void FakeComposerClient::onSurfaceFlingerStart() {
-    mSurfaceComposer = nullptr;
-    do {
-        mSurfaceComposer = android::sp<android::SurfaceComposerClient>::make();
-        android::status_t initResult = mSurfaceComposer->initCheck();
-        if (initResult != android::NO_ERROR) {
-            ALOGD("Init result: %d", initResult);
-            mSurfaceComposer = nullptr;
-            std::this_thread::sleep_for(10ms);
-        }
-    } while (mSurfaceComposer == nullptr);
-    ALOGD("SurfaceComposerClient created");
-    mSurfaceComposer->enableVSyncInjections(true);
-}
-
-void FakeComposerClient::onSurfaceFlingerStop() {
-    mSurfaceComposer->enableVSyncInjections(false);
-    mSurfaceComposer->dispose();
-    mSurfaceComposer.clear();
-}
-
-// Includes destroyed layers, stored in order of creation.
-int FakeComposerClient::getLayerCount() const {
-    return mLayers.size();
-}
-
-Layer FakeComposerClient::getLayer(size_t index) const {
-    // NOTE: If/when passing calls through to actual implementation,
-    // this might get more involving.
-    return static_cast<Layer>(index);
-}
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
deleted file mode 100644
index 600e765..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ /dev/null
@@ -1,301 +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 <chrono>
-
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-#include <composer-hal/2.4/ComposerClient.h>
-#include <utils/Condition.h>
-
-#include "MockComposerHal.h"
-#include "RenderState.h"
-
-using namespace android::hardware::graphics::common;
-using namespace android::hardware::graphics::composer;
-using namespace android::hardware::graphics::composer::V2_4;
-using namespace android::hardware::graphics::composer::V2_4::hal;
-using namespace android::hardware;
-using namespace std::chrono_literals;
-
-namespace {
-class LayerImpl;
-class Frame;
-class DelayedEventGenerator;
-} // namespace
-
-namespace android {
-class SurfaceComposerClient;
-} // namespace android
-
-namespace sftest {
-// NOTE: The ID's need to be exactly these. VR composer and parts of
-// the SurfaceFlinger assume the display IDs to have these values
-// despite the enum being documented as a display type.
-// TODO: Reference to actual documentation
-constexpr Display PRIMARY_DISPLAY = static_cast<Display>(HWC_DISPLAY_PRIMARY);
-constexpr Display EXTERNAL_DISPLAY = static_cast<Display>(HWC_DISPLAY_EXTERNAL);
-
-class FakeComposerClient : public ComposerHal {
-public:
-    FakeComposerClient();
-    virtual ~FakeComposerClient();
-
-    void setMockHal(MockComposerHal* mockHal) { mMockHal = mockHal; }
-
-    bool hasCapability(hwc2_capability_t capability) override;
-
-    std::string dumpDebugInfo() override;
-    void registerEventCallback(EventCallback* callback) override;
-    void unregisterEventCallback() override;
-
-    uint32_t getMaxVirtualDisplayCount() override;
-    V2_1::Error createVirtualDisplay(uint32_t width, uint32_t height, V1_0::PixelFormat* format,
-                                     Display* outDisplay) override;
-    V2_1::Error destroyVirtualDisplay(Display display) override;
-    V2_1::Error createLayer(Display display, Layer* outLayer) override;
-    V2_1::Error destroyLayer(Display display, Layer layer) override;
-
-    V2_1::Error getActiveConfig(Display display, Config* outConfig) override;
-    V2_1::Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
-                                       V1_0::PixelFormat format,
-                                       V1_0::Dataspace dataspace) override;
-    V2_1::Error getColorModes(Display display, hidl_vec<V1_0::ColorMode>* outModes) override;
-    V2_1::Error getDisplayAttribute(Display display, Config config,
-                                    V2_1::IComposerClient::Attribute attribute,
-                                    int32_t* outValue) override;
-    V2_1::Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
-    V2_1::Error getDisplayName(Display display, hidl_string* outName) override;
-    V2_1::Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
-    V2_1::Error getDozeSupport(Display display, bool* outSupport) override;
-    V2_1::Error getHdrCapabilities(Display display, hidl_vec<V1_0::Hdr>* outTypes,
-                                   float* outMaxLuminance, float* outMaxAverageLuminance,
-                                   float* outMinLuminance) override;
-
-    V2_1::Error setActiveConfig(Display display, Config config) override;
-    V2_1::Error setColorMode(Display display, V1_0::ColorMode mode) override;
-    V2_1::Error setPowerMode(Display display, V2_1::IComposerClient::PowerMode mode) override;
-    V2_1::Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
-    V2_1::Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
-    V2_1::Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
-                                int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
-    V2_1::Error setOutputBuffer(Display display, buffer_handle_t buffer,
-                                int32_t releaseFence) override;
-    V2_1::Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
-                                std::vector<IComposerClient::Composition>* outCompositionTypes,
-                                uint32_t* outDisplayRequestMask,
-                                std::vector<Layer>* outRequestedLayers,
-                                std::vector<uint32_t>* outRequestMasks) override;
-    V2_1::Error acceptDisplayChanges(Display display) override;
-    V2_1::Error presentDisplay(Display display, int32_t* outPresentFence,
-                               std::vector<Layer>* outLayers,
-                               std::vector<int32_t>* outReleaseFences) override;
-
-    V2_1::Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
-    V2_1::Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
-                               int32_t acquireFence) override;
-    V2_1::Error setLayerSurfaceDamage(Display display, Layer layer,
-                                      const std::vector<hwc_rect_t>& damage) override;
-    V2_1::Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
-    V2_1::Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
-    V2_1::Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
-    V2_1::Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
-    V2_1::Error setLayerDisplayFrame(Display display, Layer layer,
-                                     const hwc_rect_t& frame) override;
-    V2_1::Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
-    V2_1::Error setLayerSidebandStream(Display display, Layer layer,
-                                       buffer_handle_t stream) override;
-    V2_1::Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
-    V2_1::Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
-    V2_1::Error setLayerVisibleRegion(Display display, Layer layer,
-                                      const std::vector<hwc_rect_t>& visible) override;
-    V2_1::Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
-    // Composer 2.2
-    V2_1::Error getPerFrameMetadataKeys(
-            Display display,
-            std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* outKeys) override;
-    V2_1::Error setLayerPerFrameMetadata(
-            Display display, Layer layer,
-            const std::vector<V2_2::IComposerClient::PerFrameMetadata>& metadata) override;
-
-    V2_1::Error getReadbackBufferAttributes(
-            Display display, graphics::common::V1_1::PixelFormat* outFormat,
-            graphics::common::V1_1::Dataspace* outDataspace) override;
-    V2_1::Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
-                                  android::base::unique_fd fenceFd) override;
-    V2_1::Error getReadbackBufferFence(Display display,
-                                       android::base::unique_fd* outFenceFd) override;
-    V2_1::Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
-                                         graphics::common::V1_1::PixelFormat* format,
-                                         Display* outDisplay) override;
-    V2_1::Error getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height,
-                                           graphics::common::V1_1::PixelFormat format,
-                                           graphics::common::V1_1::Dataspace dataspace) override;
-    V2_1::Error setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) override;
-
-    V2_1::Error setLayerFloatColor(Display display, Layer layer,
-                                   V2_2::IComposerClient::FloatColor color) override;
-
-    V2_1::Error getColorModes_2_2(Display display,
-                                  hidl_vec<graphics::common::V1_1::ColorMode>* outModes) override;
-    V2_1::Error getRenderIntents(
-            Display display, graphics::common::V1_1::ColorMode mode,
-            std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
-    V2_1::Error setColorMode_2_2(Display display, graphics::common::V1_1::ColorMode mode,
-                                 graphics::common::V1_1::RenderIntent intent) override;
-
-    std::array<float, 16> getDataspaceSaturationMatrix(
-            graphics::common::V1_1::Dataspace dataspace) override;
-
-    // Composer 2.3
-    V2_1::Error getPerFrameMetadataKeys_2_3(
-            Display display,
-            std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* outKeys) override;
-
-    V2_1::Error setColorMode_2_3(Display display, graphics::common::V1_2::ColorMode mode,
-                                 graphics::common::V1_1::RenderIntent intent) override;
-
-    V2_1::Error getRenderIntents_2_3(
-            Display display, graphics::common::V1_2::ColorMode mode,
-            std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
-
-    V2_1::Error getColorModes_2_3(Display display,
-                                  hidl_vec<graphics::common::V1_2::ColorMode>* outModes) override;
-
-    V2_1::Error getClientTargetSupport_2_3(Display display, uint32_t width, uint32_t height,
-                                           graphics::common::V1_2::PixelFormat format,
-                                           graphics::common::V1_2::Dataspace dataspace) override;
-    V2_1::Error getReadbackBufferAttributes_2_3(
-            Display display, graphics::common::V1_2::PixelFormat* outFormat,
-            graphics::common::V1_2::Dataspace* outDataspace) override;
-    V2_1::Error getHdrCapabilities_2_3(Display display,
-                                       hidl_vec<graphics::common::V1_2::Hdr>* outTypes,
-                                       float* outMaxLuminance, float* outMaxAverageLuminance,
-                                       float* outMinLuminance) override;
-    V2_1::Error setLayerPerFrameMetadata_2_3(
-            Display display, Layer layer,
-            const std::vector<V2_3::IComposerClient::PerFrameMetadata>& metadata) override;
-    V2_1::Error getDisplayIdentificationData(Display display, uint8_t* outPort,
-                                             std::vector<uint8_t>* outData) override;
-    V2_1::Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
-    V2_1::Error getDisplayedContentSamplingAttributes(
-            uint64_t display, graphics::common::V1_2::PixelFormat& format,
-            graphics::common::V1_2::Dataspace& dataspace,
-            hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& componentMask) override;
-    V2_1::Error setDisplayedContentSamplingEnabled(
-            uint64_t display, V2_3::IComposerClient::DisplayedContentSampling enable,
-            hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> componentMask,
-            uint64_t maxFrames) override;
-    V2_1::Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp,
-                                          uint64_t& frameCount,
-                                          hidl_vec<uint64_t>& sampleComponent0,
-                                          hidl_vec<uint64_t>& sampleComponent1,
-                                          hidl_vec<uint64_t>& sampleComponent2,
-                                          hidl_vec<uint64_t>& sampleComponent3) override;
-    V2_1::Error getDisplayCapabilities(
-            Display display,
-            std::vector<V2_3::IComposerClient::DisplayCapability>* outCapabilities) override;
-    V2_1::Error setLayerPerFrameMetadataBlobs(
-            Display display, Layer layer,
-            std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& blobs) override;
-    V2_1::Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
-    V2_1::Error setDisplayBrightness(Display display, float brightness) override;
-
-    // Composer 2.4
-    void registerEventCallback_2_4(EventCallback_2_4* callback) override;
-
-    void unregisterEventCallback_2_4() override;
-
-    V2_4::Error getDisplayCapabilities_2_4(
-            Display display,
-            std::vector<V2_4::IComposerClient::DisplayCapability>* outCapabilities) override;
-    V2_4::Error getDisplayConnectionType(
-            Display display, V2_4::IComposerClient::DisplayConnectionType* outType) override;
-    V2_4::Error getDisplayAttribute_2_4(Display display, Config config,
-                                        IComposerClient::Attribute attribute,
-                                        int32_t* outValue) override;
-    V2_4::Error getDisplayVsyncPeriod(Display display,
-                                      V2_4::VsyncPeriodNanos* outVsyncPeriod) override;
-    V2_4::Error setActiveConfigWithConstraints(
-            Display display, Config config,
-            const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
-            VsyncPeriodChangeTimeline* outTimeline) override;
-    V2_4::Error setAutoLowLatencyMode(Display display, bool on) override;
-    V2_4::Error getSupportedContentTypes(
-            Display display,
-            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
-    V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override;
-    V2_4::Error validateDisplay_2_4(
-            Display display, std::vector<Layer>* outChangedLayers,
-            std::vector<IComposerClient::Composition>* outCompositionTypes,
-            uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
-            std::vector<uint32_t>* outRequestMasks,
-            IComposerClient::ClientTargetProperty* outClientTargetProperty) 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;
-
-    void setClient(ComposerClient* client);
-
-    void requestVSync(uint64_t vsyncTime = 0);
-    // We don't want tests hanging, so always use a timeout. Remember
-    // to always check the number of frames with test ASSERT_!
-    // Wait until next frame is rendered after requesting vsync.
-    void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms);
-    void runVSyncAfter(std::chrono::nanoseconds wait);
-
-    int getFrameCount() const;
-    // We don't want tests hanging, so always use a timeout. Remember
-    // to always check the number of frames with test ASSERT_!
-    void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const;
-    std::vector<RenderState> getFrameRects(int frame) const;
-    std::vector<RenderState> getLatestFrame() const;
-    void clearFrames();
-
-    void onSurfaceFlingerStart();
-    void onSurfaceFlingerStop();
-
-    int getLayerCount() const;
-    Layer getLayer(size_t index) const;
-
-    void hotplugDisplay(Display display, IComposerCallback::Connection state);
-    void refreshDisplay(Display display);
-
-private:
-    LayerImpl& getLayerImpl(Layer handle);
-
-    EventCallback* mEventCallback;
-    EventCallback_2_4* mEventCallback_2_4;
-    Config mCurrentConfig;
-    bool mVsyncEnabled;
-    std::vector<std::unique_ptr<LayerImpl>> mLayers;
-    std::vector<std::unique_ptr<Frame>> mFrames;
-    // Using a pointer to hide the implementation into the CPP file.
-    std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator;
-    android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
-    mutable android::Mutex mStateMutex;
-    mutable android::Condition mFramesAvailable;
-
-    MockComposerHal* mMockHal = nullptr;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
deleted file mode 100644
index c656eed..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeHwcService"
-#include <log/log.h>
-
-#include "FakeComposerService.h"
-
-using namespace android::hardware;
-using namespace android::hardware::graphics::composer;
-
-namespace sftest {
-
-FakeComposerService_2_1::FakeComposerService_2_1(android::sp<ComposerClient>& client)
-      : mClient(client) {}
-
-FakeComposerService_2_1::~FakeComposerService_2_1() {
-    ALOGI("Maybe killing client %p", mClient.get());
-    // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_1::getCapabilities(getCapabilities_cb hidl_cb) {
-    ALOGI("FakeComposerService::getCapabilities");
-    hidl_cb(hidl_vec<Capability>());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_1::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-    ALOGI("FakeComposerService::dumpDebugInfo");
-    hidl_cb(hidl_string());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_1::createClient(createClient_cb hidl_cb) {
-    ALOGI("FakeComposerService::createClient %p", mClient.get());
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::NONE, mClient);
-    return Void();
-}
-
-FakeComposerService_2_2::FakeComposerService_2_2(android::sp<ComposerClient>& client)
-      : mClient(client) {}
-
-FakeComposerService_2_2::~FakeComposerService_2_2() {
-    ALOGI("Maybe killing client %p", mClient.get());
-    // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_2::getCapabilities(getCapabilities_cb hidl_cb) {
-    ALOGI("FakeComposerService::getCapabilities");
-    hidl_cb(hidl_vec<Capability>());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_2::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-    ALOGI("FakeComposerService::dumpDebugInfo");
-    hidl_cb(hidl_string());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_2::createClient(createClient_cb hidl_cb) {
-    ALOGI("FakeComposerService::createClient %p", mClient.get());
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::NONE, mClient);
-    return Void();
-}
-
-FakeComposerService_2_3::FakeComposerService_2_3(android::sp<ComposerClient>& client)
-      : mClient(client) {}
-
-FakeComposerService_2_3::~FakeComposerService_2_3() {
-    ALOGI("Maybe killing client %p", mClient.get());
-    // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_3::getCapabilities(getCapabilities_cb hidl_cb) {
-    ALOGI("FakeComposerService::getCapabilities");
-    hidl_cb(hidl_vec<Capability>());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_3::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-    ALOGI("FakeComposerService::dumpDebugInfo");
-    hidl_cb(hidl_string());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_3::createClient(createClient_cb hidl_cb) {
-    LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_3");
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
-    return Void();
-}
-
-Return<void> FakeComposerService_2_3::createClient_2_3(createClient_2_3_cb hidl_cb) {
-    ALOGI("FakeComposerService_2_3::createClient_2_3 %p", mClient.get());
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::NONE, mClient);
-    return Void();
-}
-
-FakeComposerService_2_4::FakeComposerService_2_4(android::sp<ComposerClient>& client)
-      : mClient(client) {}
-
-FakeComposerService_2_4::~FakeComposerService_2_4() {
-    ALOGI("Maybe killing client %p", mClient.get());
-    // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_4::getCapabilities(getCapabilities_cb hidl_cb) {
-    ALOGI("FakeComposerService::getCapabilities");
-    hidl_cb(hidl_vec<Capability>());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_4::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-    ALOGI("FakeComposerService::dumpDebugInfo");
-    hidl_cb(hidl_string());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient(createClient_cb hidl_cb) {
-    LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_4");
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
-    return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient_2_3(createClient_2_3_cb hidl_cb) {
-    LOG_ALWAYS_FATAL("createClient_2_3 called on FakeComposerService_2_4");
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
-    return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient_2_4(createClient_2_4_cb hidl_cb) {
-    ALOGI("FakeComposerService_2_4::createClient_2_4 %p", mClient.get());
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_4::Error::NONE, mClient);
-    return Void();
-}
-
-} // namespace sftest
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
deleted file mode 100644
index 47f970f..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 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/hardware/graphics/composer/2.4/IComposer.h>
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-#include <composer-hal/2.4/ComposerClient.h>
-
-using android::hardware::Return;
-
-using ComposerClient = android::hardware::graphics::composer::V2_4::hal::ComposerClient;
-
-namespace sftest {
-
-using IComposer_2_1 = android::hardware::graphics::composer::V2_1::IComposer;
-
-class FakeComposerService_2_1 : public IComposer_2_1 {
-public:
-    explicit FakeComposerService_2_1(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService_2_1();
-
-    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-    Return<void> createClient(createClient_cb hidl_cb) override;
-
-private:
-    android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_2 = android::hardware::graphics::composer::V2_2::IComposer;
-class FakeComposerService_2_2 : public IComposer_2_2 {
-public:
-    explicit FakeComposerService_2_2(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService_2_2();
-
-    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-    Return<void> createClient(createClient_cb hidl_cb) override;
-
-private:
-    android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_3 = android::hardware::graphics::composer::V2_3::IComposer;
-class FakeComposerService_2_3 : public IComposer_2_3 {
-public:
-    explicit FakeComposerService_2_3(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService_2_3();
-
-    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-    Return<void> createClient(createClient_cb hidl_cb) override;
-    Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
-
-private:
-    android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_4 = android::hardware::graphics::composer::V2_4::IComposer;
-
-class FakeComposerService_2_4 : public IComposer_2_4 {
-public:
-    explicit FakeComposerService_2_4(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService_2_4();
-
-    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-    Return<void> createClient(createClient_cb hidl_cb) override;
-    Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
-    Return<void> createClient_2_4(createClient_2_4_cb hidl_cb) override;
-
-private:
-    android::sp<ComposerClient> mClient;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
deleted file mode 100644
index 1cea25a..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ /dev/null
@@ -1,193 +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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeHwcUtil"
-#include <log/log.h>
-
-#include "FakeComposerUtils.h"
-#include "RenderState.h"
-
-#include "SurfaceFlinger.h" // Get the name of the service...
-
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <hidl/ServiceManagement.h>
-
-#include <iomanip>
-#include <thread>
-
-using android::String16;
-using android::sp;
-using namespace std::chrono_literals;
-using namespace sftest;
-using std::setw;
-
-namespace sftest {
-
-// clang-format off
-inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) {
-    os << std::fixed << std::setprecision(1) << "("
-       << setw(align) << sourceRect.left << setw(0) << ","
-       << setw(align) << sourceRect.top << setw(0) << ","
-       << setw(align) << sourceRect.right << setw(0) << ","
-       << setw(align) << sourceRect.bottom << setw(0) << ")";
-}
-
-inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) {
-    os << "("
-       << setw(align) << displayRect.left << setw(0) << ","
-       << setw(align) << displayRect.top << setw(0) << ","
-       << setw(align) << displayRect.right << setw(0) << ","
-       << setw(align) << displayRect.bottom << setw(0) << ")";
-}
-// clang-format on
-
-inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) {
-    printSourceRectAligned(os, state.mSourceCrop, 7);
-    os << "->";
-    printDisplayRectAligned(os, state.mDisplayFrame, 5);
-    return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3)
-              << state.mPlaneAlpha << " Xform:" << state.mTransform;
-}
-
-// Helper for verifying the parts of the RenderState
-template <typename T>
-bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val,
-                 const char* name) {
-    if (ref != val) {
-        message = message << "Expected " << name << ":" << ref << ", got:" << val << ".";
-        return false;
-    }
-    return true;
-}
-
-::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) {
-    // TODO: Message could start as success and be assigned as failure.
-    // Only problem is that utility assumes it to be failure and just adds stuff. Would
-    // need still special case the initial failure in the utility?
-    // TODO: ... or would it be possible to break this back to gtest primitives?
-    ::testing::AssertionResult message = ::testing::AssertionFailure();
-    bool passes = true;
-
-    // The work here is mostly about providing good log strings for differences
-    passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame");
-    passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha");
-    passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count");
-    passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop");
-    // ... add more
-    if (passes) {
-        return ::testing::AssertionSuccess();
-    }
-    return message;
-}
-
-::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
-                                         const std::vector<RenderState>& val) {
-    ::testing::AssertionResult message = ::testing::AssertionFailure();
-    bool passed = true;
-    if (ref.size() != val.size()) {
-        message << "Expected " << ref.size() << " rects, got " << val.size() << ".";
-        passed = false;
-    }
-    for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) {
-        ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]);
-        if (rectResult == false) {
-            message << "First different rect at " << rectIndex << ": " << rectResult.message();
-            passed = false;
-            break;
-        }
-    }
-
-    if (passed) {
-        return ::testing::AssertionSuccess();
-    } else {
-        message << "\nReference:";
-        for (auto state = ref.begin(); state != ref.end(); ++state) {
-            message << "\n" << *state;
-        }
-        message << "\nActual:";
-        for (auto state = val.begin(); state != val.end(); ++state) {
-            message << "\n" << *state;
-        }
-    }
-    return message;
-}
-
-void startSurfaceFlinger() {
-    ALOGI("Start SurfaceFlinger");
-    system("start surfaceflinger");
-
-    sp<android::IServiceManager> sm(android::defaultServiceManager());
-    sp<android::IBinder> sf;
-    while (sf == nullptr) {
-        std::this_thread::sleep_for(10ms);
-        sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
-    }
-    ALOGV("SurfaceFlinger running");
-}
-
-void stopSurfaceFlinger() {
-    ALOGI("Stop SurfaceFlinger");
-    system("stop surfaceflinger");
-    sp<android::IServiceManager> sm(android::defaultServiceManager());
-    sp<android::IBinder> sf;
-    while (sf != nullptr) {
-        std::this_thread::sleep_for(10ms);
-        sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
-    }
-    ALOGV("SurfaceFlinger stopped");
-}
-
-////////////////////////////////////////////////
-
-void FakeHwcEnvironment::SetUp() {
-    ALOGI("Test env setup");
-    system("setenforce 0");
-    system("stop");
-    property_set("debug.sf.nobootanimation", "1");
-    {
-        char value[PROPERTY_VALUE_MAX];
-        property_get("debug.sf.nobootanimation", value, "0");
-        LOG_FATAL_IF(atoi(value) != 1, "boot skip not set");
-    }
-    // TODO: Try registering the mock as the default service instead.
-    property_set("debug.sf.hwc_service_name", "mock");
-
-    // This allows tests/SF to register/load a HIDL service not listed in manifest files.
-    android::hardware::details::setTrebleTestingOverride(true);
-    property_set("debug.sf.treble_testing_override", "true");
-}
-
-void FakeHwcEnvironment::TearDown() {
-    ALOGI("Test env tear down");
-    system("stop");
-    // Wait for mock call signaling teardown?
-    property_set("debug.sf.nobootanimation", "0");
-    property_set("debug.sf.hwc_service_name", "default");
-    system("setenforce 1");
-    ALOGI("Test env tear down - done");
-}
-
-} // namespace sftest
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
deleted file mode 100644
index 383a111..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
+++ /dev/null
@@ -1,115 +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 "FakeComposerClient.h"
-
-#include <gui/SurfaceComposerClient.h>
-#include <log/log.h>
-#include <gtest/gtest.h>
-
-// clang-format off
-// Note: This needs to reside in the global namespace for the GTest to use it
-inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) {
-    return os << "(" << rect.left << ","
-              << rect.top << ","
-              << rect.right << ","
-              << rect.bottom << ")";
-}
-
-inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) {
-    return os << "(" << rect.left << ","
-              << rect.top << ","
-              << rect.right << ","
-              << rect.bottom << ")";
-}
-// clang-format on
-
-namespace sftest {
-
-class RenderState;
-
-// clang-format off
-inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) {
-    return a.top == b.top &&
-            a.left == b.left &&
-            a.bottom == b.bottom &&
-            a.right == b.right;
-}
-
-inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) {
-    return a.top == b.top &&
-            a.left == b.left &&
-            a.bottom == b.bottom &&
-            a.right == b.right;
-}
-// clang-format on
-
-inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) {
-    return !(a == b);
-}
-
-inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) {
-    return !(a == b);
-}
-
-::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val);
-::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
-                                         const std::vector<RenderState>& val);
-
-void startSurfaceFlinger();
-void stopSurfaceFlinger();
-
-class FakeHwcEnvironment : public ::testing::Environment {
-public:
-    virtual ~FakeHwcEnvironment() {}
-    void SetUp() override;
-    void TearDown() override;
-};
-
-/*
- * All surface state changes are supposed to happen inside a global
- * transaction. TransactionScope object at the beginning of
- * scope automates the process. The resulting scope gives a visual cue
- * on the span of the transaction as well.
- *
- * Closing the transaction is synchronous, i.e., it waits for
- * SurfaceFlinger to composite one frame. Now, the FakeComposerClient
- * is built to explicitly request vsyncs one at the time. A delayed
- * request must be made before closing the transaction or the test
- * thread stalls until SurfaceFlinger does an emergency vsync by
- * itself. TransactionScope encapsulates this vsync magic.
- */
-class TransactionScope : public android::SurfaceComposerClient::Transaction {
-public:
-    explicit TransactionScope(FakeComposerClient& composer) : Transaction(), mComposer(composer) {}
-
-    ~TransactionScope() {
-        int frameCount = mComposer.getFrameCount();
-        mComposer.runVSyncAfter(1ms);
-        LOG_ALWAYS_FATAL_IF(android::NO_ERROR != apply());
-        // Make sure that exactly one frame has been rendered.
-        mComposer.waitUntilFrame(frameCount + 1);
-        //        LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
-        //                            "Unexpected frame advance. Delta: %d",
-        //                            mComposer.getFrameCount() - frameCount);
-    }
-
-    FakeComposerClient& mComposer;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
deleted file mode 100644
index 5dc3778..0000000
--- a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
+++ /dev/null
@@ -1,47 +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 <composer-hal/2.4/ComposerClient.h>
-
-#include <gmock/gmock.h>
-
-using namespace android::hardware::graphics::common;
-using namespace android::hardware::graphics::composer;
-using namespace android::hardware::graphics::composer::V2_4;
-using namespace android::hardware::graphics::composer::V2_4::hal;
-using namespace android::hardware;
-using namespace std::chrono_literals;
-
-namespace sftest {
-
-// Mock class for ComposerHal. Implements only the functions used in the test.
-class MockComposerHal {
-public:
-    MOCK_METHOD2(getActiveConfig, V2_1::Error(Display, Config*));
-    MOCK_METHOD4(getDisplayAttribute_2_4,
-                 V2_4::Error(Display, Config, V2_4::IComposerClient::Attribute, int32_t*));
-    MOCK_METHOD2(getDisplayConfigs, V2_1::Error(Display, hidl_vec<Config>*));
-    MOCK_METHOD2(setActiveConfig, V2_1::Error(Display, Config));
-    MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, V2_4::VsyncPeriodNanos*));
-    MOCK_METHOD4(setActiveConfigWithConstraints,
-                 V2_4::Error(Display, Config,
-                             const V2_4::IComposerClient::VsyncPeriodChangeConstraints&,
-                             VsyncPeriodChangeTimeline*));
-};
-
-} // namespace sftest
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h
deleted file mode 100644
index 40193f2..0000000
--- a/services/surfaceflinger/tests/fakehwc/RenderState.h
+++ /dev/null
@@ -1,42 +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 <vector>
-
-namespace sftest {
-// Description of a rendered rectangle.  Should only contain
-// instructions necessary to rasterize the rectangle. The full scene
-// is given as a sorted list of rectangles, bottom layer at index 0.
-class RenderState {
-public:
-    RenderState() = default;
-    // Default copy-ctor
-
-    hwc_rect_t mDisplayFrame = {0, 0, 0, 0};
-    hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f};
-    std::vector<hwc_rect_t> mVisibleRegion;
-    hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE;
-    buffer_handle_t mBuffer = 0;
-    uint32_t mSwapCount = 0;   // How many set buffer calls to the layer.
-    int32_t mAcquireFence = 0; // Probably should not be here.
-    float mPlaneAlpha = 0.f;
-    hwc_color_t mLayerColor = {0, 0, 0, 0};
-    hwc_transform_t mTransform = static_cast<hwc_transform_t>(0);
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
deleted file mode 100644
index 1d3401a..0000000
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ /dev/null
@@ -1,1794 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-// 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 "FakeHwcTest"
-
-#include "FakeComposerClient.h"
-#include "FakeComposerService.h"
-#include "FakeComposerUtils.h"
-#include "MockComposerHal.h"
-
-#include <binder/Parcel.h>
-#include <gui/AidlStatusUtil.h>
-#include <gui/DisplayEventReceiver.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <android/hidl/manager/1.0/IServiceManager.h>
-#include <android/looper.h>
-#include <android/native_window.h>
-#include <binder/ProcessState.h>
-#include <hwbinder/ProcessState.h>
-#include <log/log.h>
-#include <private/gui/ComposerService.h>
-#include <private/gui/ComposerServiceAIDL.h>
-#include <ui/DisplayMode.h>
-#include <ui/DynamicDisplayInfo.h>
-#include <utils/Looper.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <limits>
-#include <thread>
-
-using namespace std::chrono_literals;
-
-using namespace android;
-using namespace android::hardware;
-
-using namespace sftest;
-
-namespace {
-
-// Mock test helpers
-using ::testing::_;
-using ::testing::DoAll;
-using ::testing::Return;
-using ::testing::SetArgPointee;
-
-using Transaction = SurfaceComposerClient::Transaction;
-using Attribute = V2_4::IComposerClient::Attribute;
-using Display = V2_1::Display;
-
-///////////////////////////////////////////////
-constexpr PhysicalDisplayId physicalIdFromHwcDisplayId(Display hwcId) {
-    return PhysicalDisplayId::fromPort(hwcId);
-}
-constexpr PhysicalDisplayId kPrimaryDisplayId = physicalIdFromHwcDisplayId(PRIMARY_DISPLAY);
-constexpr PhysicalDisplayId kExternalDisplayId = physicalIdFromHwcDisplayId(EXTERNAL_DISPLAY);
-
-struct TestColor {
-public:
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    uint8_t a;
-};
-
-constexpr static TestColor RED = {195, 63, 63, 255};
-constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
-constexpr static TestColor GREEN = {63, 195, 63, 255};
-constexpr static TestColor BLUE = {63, 63, 195, 255};
-constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color,
-                             bool unlock = true) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = sc->getSurface();
-    ASSERT_TRUE(s != nullptr);
-    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
-    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
-            pixel[0] = color.r;
-            pixel[1] = color.g;
-            pixel[2] = color.b;
-            pixel[3] = color.a;
-        }
-    }
-    if (unlock) {
-        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-    }
-}
-
-inline RenderState makeSimpleRect(int left, int top, int right, int bottom) {
-    RenderState res;
-    res.mDisplayFrame = hwc_rect_t{left, top, right, bottom};
-    res.mPlaneAlpha = 1.0f;
-    res.mSwapCount = 0;
-    res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left),
-                                  static_cast<float>(bottom - top)};
-    return res;
-}
-
-inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right,
-                                  unsigned int bottom) {
-    EXPECT_LE(left, static_cast<unsigned int>(INT_MAX));
-    EXPECT_LE(top, static_cast<unsigned int>(INT_MAX));
-    EXPECT_LE(right, static_cast<unsigned int>(INT_MAX));
-    EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX));
-    return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right),
-                          static_cast<int>(bottom));
-}
-
-///////////////////////////////////////////////
-template <typename FakeComposerService>
-class DisplayTest : public ::testing::Test {
-protected:
-    struct TestConfig {
-        int32_t id;
-        int32_t w;
-        int32_t h;
-        int32_t vsyncPeriod;
-        int32_t group;
-    };
-
-    static int processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
-        auto self = static_cast<DisplayTest*>(data);
-
-        ssize_t n;
-        DisplayEventReceiver::Event buffer[1];
-
-        while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
-            for (int i = 0; i < n; i++) {
-                self->mReceivedDisplayEvents.push_back(buffer[i]);
-            }
-        }
-        ALOGD_IF(n < 0, "Error reading events (%s)", strerror(-n));
-        return 1;
-    }
-
-    Error getDisplayAttributeNoMock(Display display, Config config,
-                                    V2_4::IComposerClient::Attribute attribute, int32_t* outValue) {
-        mFakeComposerClient->setMockHal(nullptr);
-        auto ret =
-                mFakeComposerClient->getDisplayAttribute_2_4(display, config, attribute, outValue);
-        mFakeComposerClient->setMockHal(mMockComposer.get());
-        return ret;
-    }
-
-    void setExpectationsForConfigs(Display display, std::vector<TestConfig> testConfigs,
-                                   Config activeConfig, V2_4::VsyncPeriodNanos defaultVsyncPeriod) {
-        std::vector<Config> configIds;
-        for (size_t i = 0; i < testConfigs.size(); i++) {
-            configIds.push_back(testConfigs[i].id);
-
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::WIDTH, _))
-                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].w), Return(Error::NONE)));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::HEIGHT, _))
-                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].h), Return(Error::NONE)));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::VSYNC_PERIOD,
-                                                _))
-                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].vsyncPeriod),
-                                          Return(Error::NONE)));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::CONFIG_GROUP,
-                                                _))
-                    .WillRepeatedly(
-                            DoAll(SetArgPointee<3>(testConfigs[i].group), Return(Error::NONE)));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_X, _))
-                    .WillRepeatedly(Return(Error::UNSUPPORTED));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_Y, _))
-                    .WillRepeatedly(Return(Error::UNSUPPORTED));
-        }
-
-        EXPECT_CALL(*mMockComposer, getDisplayConfigs(display, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(hidl_vec<Config>(configIds)),
-                                      Return(V2_1::Error::NONE)));
-
-        EXPECT_CALL(*mMockComposer, getActiveConfig(display, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(activeConfig), Return(V2_1::Error::NONE)));
-
-        EXPECT_CALL(*mMockComposer, getDisplayVsyncPeriod(display, _))
-                .WillRepeatedly(
-                        DoAll(SetArgPointee<1>(defaultVsyncPeriod), Return(V2_4::Error::NONE)));
-    }
-
-    void SetUp() override {
-        mMockComposer = std::make_unique<MockComposerHal>();
-        mFakeComposerClient = new FakeComposerClient();
-        mFakeComposerClient->setMockHal(mMockComposer.get());
-
-        auto client = sp<V2_4::hal::ComposerClient>::make(mFakeComposerClient);
-        mFakeService = sp<FakeComposerService>::make(client);
-        ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
-
-        android::hardware::ProcessState::self()->startThreadPool();
-        android::ProcessState::self()->startThreadPool();
-
-        setExpectationsForConfigs(PRIMARY_DISPLAY,
-                                  {{
-                                          .id = 1,
-                                          .w = 1920,
-                                          .h = 1024,
-                                          .vsyncPeriod = 16'666'666,
-                                          .group = 0,
-                                  }},
-                                  1, 16'666'666);
-
-        startSurfaceFlinger();
-
-        // Fake composer wants to enable VSync injection
-        mFakeComposerClient->onSurfaceFlingerStart();
-
-        mComposerClient = sp<SurfaceComposerClient>::make();
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        mReceiver.reset(
-                new DisplayEventReceiver(gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
-                                         gui::ISurfaceComposer::EventRegistration::modeChanged));
-        mLooper = sp<Looper>::make(false);
-        mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
-    }
-
-    void TearDown() override {
-        mLooper = nullptr;
-        mReceiver = nullptr;
-
-        mComposerClient->dispose();
-        mComposerClient = nullptr;
-
-        // Fake composer needs to release SurfaceComposerClient before the stop.
-        mFakeComposerClient->onSurfaceFlingerStop();
-        stopSurfaceFlinger();
-
-        mFakeComposerClient->setMockHal(nullptr);
-
-        mFakeService = nullptr;
-        // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
-        // management.
-        mMockComposer = nullptr;
-    }
-
-    void waitForDisplayTransaction(Display display) {
-        // Both a refresh and a vsync event are needed to apply pending display
-        // transactions.
-        mFakeComposerClient->refreshDisplay(display);
-        mFakeComposerClient->runVSyncAndWait();
-
-        // Extra vsync and wait to avoid a 10% flake due to a race.
-        mFakeComposerClient->runVSyncAndWait();
-    }
-
-    bool waitForHotplugEvent(Display displayId, bool connected) {
-        return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected);
-    }
-
-    bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-        int waitCount = 20;
-        while (waitCount--) {
-            while (!mReceivedDisplayEvents.empty()) {
-                auto event = mReceivedDisplayEvents.front();
-                mReceivedDisplayEvents.pop_front();
-
-                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
-                         "event hotplug: displayId %s, connected %d",
-                         to_string(event.header.displayId).c_str(), event.hotplug.connected);
-
-                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
-                    event.header.displayId == displayId && event.hotplug.connected == connected) {
-                    return true;
-                }
-            }
-
-            mLooper->pollOnce(1);
-        }
-        return false;
-    }
-
-    bool waitForModeChangedEvent(Display display, int32_t modeId) {
-        PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display);
-        int waitCount = 20;
-        while (waitCount--) {
-            while (!mReceivedDisplayEvents.empty()) {
-                auto event = mReceivedDisplayEvents.front();
-                mReceivedDisplayEvents.pop_front();
-
-                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
-                         "event mode: displayId %s, modeId %d",
-                         to_string(event.header.displayId).c_str(), event.modeChange.modeId);
-
-                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE &&
-                    event.header.displayId == displayId && event.modeChange.modeId == modeId) {
-                    return true;
-                }
-            }
-
-            mLooper->pollOnce(1);
-        }
-        return false;
-    }
-
-    void Test_HotplugOneConfig() {
-        ALOGD("DisplayTest::Test_Hotplug_oneConfig");
-
-        setExpectationsForConfigs(EXTERNAL_DISPLAY,
-                                  {{.id = 1,
-                                    .w = 200,
-                                    .h = 400,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 0}},
-                                  1, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-        {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-            EXPECT_FALSE(display == nullptr);
-
-            ui::DisplayMode mode;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-            const ui::Size& resolution = mode.resolution;
-            EXPECT_EQ(ui::Size(200, 400), resolution);
-            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        mFakeComposerClient->clearFrames();
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-
-        {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-            EXPECT_TRUE(display == nullptr);
-
-            ui::DisplayMode mode;
-            EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        }
-    }
-
-    void Test_HotplugTwoSeparateConfigs() {
-        ALOGD("DisplayTest::Test_HotplugTwoSeparateConfigs");
-
-        setExpectationsForConfigs(EXTERNAL_DISPLAY,
-                                  {{.id = 1,
-                                    .w = 200,
-                                    .h = 400,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 0},
-                                   {.id = 2,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 1}},
-                                  1, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-        EXPECT_FALSE(display == nullptr);
-
-        ui::DisplayMode mode;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(200, 400), mode.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        ui::DynamicDisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-        const auto& modes = info.supportedDisplayModes;
-        EXPECT_EQ(modes.size(), 2);
-
-        // change active mode
-
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 2))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (int i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.resolution.getWidth() == 800) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        mFakeComposerClient->clearFrames();
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-    }
-
-    void Test_HotplugTwoConfigsSameGroup() {
-        ALOGD("DisplayTest::Test_HotplugTwoConfigsSameGroup");
-
-        setExpectationsForConfigs(EXTERNAL_DISPLAY,
-                                  {{.id = 2,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 31},
-                                   {.id = 3,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 31}},
-                                  2, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-        EXPECT_FALSE(display == nullptr);
-
-        ui::DisplayMode mode;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        ui::DynamicDisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-        const auto& modes = info.supportedDisplayModes;
-        EXPECT_EQ(modes.size(), 2);
-
-        // change active mode
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (int i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.refreshRate == 1e9f / 11'111'111) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        mFakeComposerClient->clearFrames();
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-    }
-
-    void Test_HotplugThreeConfigsMixedGroups() {
-        ALOGD("DisplayTest::Test_HotplugThreeConfigsMixedGroups");
-
-        setExpectationsForConfigs(EXTERNAL_DISPLAY,
-                                  {{.id = 2,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 0},
-                                   {.id = 3,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 0},
-                                   {.id = 4,
-                                    .w = 1600,
-                                    .h = 3200,
-                                    .vsyncPeriod = 8'333'333,
-                                    .group = 1},
-                                   {.id = 5,
-                                    .w = 1600,
-                                    .h = 3200,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 1}},
-                                  2, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-        EXPECT_FALSE(display == nullptr);
-
-        ui::DisplayMode mode;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        ui::DynamicDisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-        const auto& modes = info.supportedDisplayModes;
-        EXPECT_EQ(modes.size(), 4);
-
-        // change active mode to 800x1600@90Hz
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (size_t i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.resolution.getWidth() == 800 && mode.refreshRate == 1e9f / 11'111'111) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            modes[i].refreshRate,
-                                                                            modes[i].refreshRate,
-                                                                            modes[i].refreshRate,
-                                                                            modes[i].refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        // change active mode to 1600x3200@120Hz
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 4))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (int i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.refreshRate == 1e9f / 8'333'333) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
-        EXPECT_EQ(1e9f / 8'333'333, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        // change active mode to 1600x3200@90Hz
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 5))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (int i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.resolution.getWidth() == 1600 && mode.refreshRate == 1e9f / 11'111'111) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        mFakeComposerClient->clearFrames();
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-    }
-
-    void Test_HotplugPrimaryDisplay() {
-        ALOGD("DisplayTest::HotplugPrimaryDisplay");
-
-        mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-
-        waitForDisplayTransaction(PRIMARY_DISPLAY);
-
-        EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
-        {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
-            EXPECT_TRUE(display == nullptr);
-
-            ui::DisplayMode mode;
-            auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
-            EXPECT_NE(NO_ERROR, result);
-        }
-
-        mFakeComposerClient->clearFrames();
-
-        setExpectationsForConfigs(PRIMARY_DISPLAY,
-                                  {{.id = 1,
-                                    .w = 400,
-                                    .h = 200,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 0}},
-                                  1, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-
-        waitForDisplayTransaction(PRIMARY_DISPLAY);
-
-        EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
-
-        {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
-            EXPECT_FALSE(display == nullptr);
-
-            ui::DisplayMode mode;
-            auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
-            EXPECT_EQ(NO_ERROR, result);
-            ASSERT_EQ(ui::Size(400, 200), mode.resolution);
-            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-        }
-    }
-
-    void Test_SubsequentHotplugConnectUpdatesDisplay(Display hwcDisplayId) {
-        ALOGD("DisplayTest::Test_SubsequentHotplugConnectUpdatesDisplay");
-
-        // Send a hotplug connected event to set up the initial display modes.
-        // The primary display is already connected so this will update it.
-        // If we're running the test of an external display this will create it.
-        setExpectationsForConfigs(hwcDisplayId,
-                                  {{.id = 1,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 1}},
-                                  /* activeConfig */ 1, 11'111'111);
-
-        mFakeComposerClient->hotplugDisplay(hwcDisplayId,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(hwcDisplayId);
-        EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true));
-
-        const auto displayId = physicalIdFromHwcDisplayId(hwcDisplayId);
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(displayId);
-        EXPECT_FALSE(display == nullptr);
-
-        // Verify that the active mode and the supported moded are updated
-        {
-            ui::DisplayMode mode;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-            EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-            ui::DynamicDisplayInfo info;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-            const auto& modes = info.supportedDisplayModes;
-            EXPECT_EQ(modes.size(), 1);
-        }
-
-        // Send another hotplug connected event
-        setExpectationsForConfigs(hwcDisplayId,
-                                  {
-                                          {.id = 1,
-                                           .w = 800,
-                                           .h = 1600,
-                                           .vsyncPeriod = 16'666'666,
-                                           .group = 1},
-                                          {.id = 2,
-                                           .w = 800,
-                                           .h = 1600,
-                                           .vsyncPeriod = 11'111'111,
-                                           .group = 1},
-                                          {.id = 3,
-                                           .w = 800,
-                                           .h = 1600,
-                                           .vsyncPeriod = 8'333'333,
-                                           .group = 1},
-                                  },
-                                  /* activeConfig */ 1, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(hwcDisplayId,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(hwcDisplayId);
-        EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true));
-
-        // Verify that the active mode and the supported moded are updated
-        {
-            ui::DisplayMode mode;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-        }
-
-        ui::DynamicDisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-        const auto& modes = info.supportedDisplayModes;
-        EXPECT_EQ(modes.size(), 3);
-
-        EXPECT_EQ(ui::Size(800, 1600), modes[0].resolution);
-        EXPECT_EQ(1e9f / 16'666'666, modes[0].refreshRate);
-
-        EXPECT_EQ(ui::Size(800, 1600), modes[1].resolution);
-        EXPECT_EQ(1e9f / 11'111'111, modes[1].refreshRate);
-
-        EXPECT_EQ(ui::Size(800, 1600), modes[2].resolution);
-        EXPECT_EQ(1e9f / 8'333'333, modes[2].refreshRate);
-
-        // Verify that we are able to switch to any of the modes
-        for (int i = modes.size() - 1; i >= 0; i--) {
-            const auto hwcId = i + 1;
-            // Set up HWC expectations for the mode change
-            if (mIs2_4Client) {
-                EXPECT_CALL(*mMockComposer,
-                            setActiveConfigWithConstraints(hwcDisplayId, hwcId, _, _))
-                        .WillOnce(Return(V2_4::Error::NONE));
-            } else {
-                EXPECT_CALL(*mMockComposer, setActiveConfig(hwcDisplayId, hwcId))
-                        .WillOnce(Return(V2_1::Error::NONE));
-            }
-
-            EXPECT_EQ(NO_ERROR,
-                      SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                        modes[i].refreshRate,
-                                                                        modes[i].refreshRate,
-                                                                        modes[i].refreshRate,
-                                                                        modes[i].refreshRate));
-            // We need to refresh twice - once to apply the pending mode change request,
-            // and once to process the change.
-            waitForDisplayTransaction(hwcDisplayId);
-            waitForDisplayTransaction(hwcDisplayId);
-            EXPECT_TRUE(waitForModeChangedEvent(hwcDisplayId, i))
-                    << "Failure while switching to mode " << i;
-
-            ui::DisplayMode mode;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-            EXPECT_EQ(modes[i].refreshRate, mode.refreshRate);
-        }
-    }
-
-    sp<V2_1::IComposer> mFakeService;
-    sp<SurfaceComposerClient> mComposerClient;
-
-    std::unique_ptr<MockComposerHal> mMockComposer;
-    FakeComposerClient* mFakeComposerClient;
-
-    std::unique_ptr<DisplayEventReceiver> mReceiver;
-    sp<Looper> mLooper;
-    std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
-
-    static constexpr bool mIs2_4Client =
-            std::is_same<FakeComposerService, FakeComposerService_2_4>::value;
-};
-
-using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
-
-// Tests that VSYNC injection can be safely toggled while invalidating.
-TEST_F(DisplayTest_2_1, VsyncInjection) {
-    const auto flinger = ComposerServiceAIDL::getComposerService();
-    bool enable = true;
-
-    for (int i = 0; i < 100; i++) {
-        flinger->enableVSyncInjections(enable);
-        enable = !enable;
-
-        constexpr uint32_t kForceInvalidate = 1004;
-        android::Parcel data, reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        EXPECT_EQ(NO_ERROR,
-                  android::IInterface::asBinder(flinger)->transact(kForceInvalidate, data, &reply));
-
-        std::this_thread::sleep_for(5ms);
-    }
-}
-
-TEST_F(DisplayTest_2_1, HotplugOneConfig) {
-    Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_1, HotplugTwoSeparateConfigs) {
-    Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_1, HotplugTwoConfigsSameGroup) {
-    Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_1, HotplugThreeConfigsMixedGroups) {
-    Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_1, HotplugPrimaryOneConfig) {
-    Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesExternalDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_2 = DisplayTest<FakeComposerService_2_2>;
-
-TEST_F(DisplayTest_2_2, HotplugOneConfig) {
-    Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_2, HotplugTwoSeparateConfigs) {
-    Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_2, HotplugTwoConfigsSameGroup) {
-    Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_2, HotplugThreeConfigsMixedGroups) {
-    Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_2, HotplugPrimaryOneConfig) {
-    Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesExternalDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_3 = DisplayTest<FakeComposerService_2_3>;
-
-TEST_F(DisplayTest_2_3, HotplugOneConfig) {
-    Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_3, HotplugTwoSeparateConfigs) {
-    Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_3, HotplugTwoConfigsSameGroup) {
-    Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_3, HotplugThreeConfigsMixedGroups) {
-    Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_3, HotplugPrimaryOneConfig) {
-    Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesExternalDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_4 = DisplayTest<FakeComposerService_2_4>;
-
-TEST_F(DisplayTest_2_4, HotplugOneConfig) {
-    Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_4, HotplugTwoSeparateConfigs) {
-    Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_4, HotplugTwoConfigsSameGroup) {
-    Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_4, HotplugThreeConfigsMixedGroups) {
-    Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_4, HotplugPrimaryOneConfig) {
-    Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesExternalDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-////////////////////////////////////////////////
-
-template <typename FakeComposerService>
-class TransactionTest : public ::testing::Test {
-protected:
-    // Layer array indexing constants.
-    constexpr static int BG_LAYER = 0;
-    constexpr static int FG_LAYER = 1;
-
-    static void SetUpTestCase() {
-        // TODO: See TODO comment at DisplayTest::SetUp for background on
-        // the lifetime of the FakeComposerClient.
-        sFakeComposer = new FakeComposerClient;
-        auto client = sp<V2_4::hal::ComposerClient>::make(sFakeComposer);
-        sp<V2_1::IComposer> fakeService = sp<FakeComposerService>::make(client);
-        (void)fakeService->registerAsService("mock");
-
-        android::hardware::ProcessState::self()->startThreadPool();
-        android::ProcessState::self()->startThreadPool();
-
-        startSurfaceFlinger();
-
-        // Fake composer wants to enable VSync injection
-        sFakeComposer->onSurfaceFlingerStart();
-    }
-
-    static void TearDownTestCase() {
-        // Fake composer needs to release SurfaceComposerClient before the stop.
-        sFakeComposer->onSurfaceFlingerStop();
-        stopSurfaceFlinger();
-        // TODO: This is deleted when the ComposerClient calls
-        // removeClient. Devise better lifetime control.
-        sFakeComposer = nullptr;
-    }
-
-    void SetUp() override {
-        ALOGI("TransactionTest::SetUp");
-        mComposerClient = sp<SurfaceComposerClient>::make();
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        ALOGI("TransactionTest::SetUp - display");
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
-        ASSERT_FALSE(display == nullptr);
-
-        ui::DisplayMode mode;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
-        const ui::Size& resolution = mode.resolution;
-        mDisplayWidth = resolution.getWidth();
-        mDisplayHeight = resolution.getHeight();
-
-        // Background surface
-        mBGSurfaceControl =
-                mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
-                                               mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mBGSurfaceControl != nullptr);
-        ASSERT_TRUE(mBGSurfaceControl->isValid());
-        fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
-
-        // Foreground surface
-        mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
-                                                           PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mFGSurfaceControl != nullptr);
-        ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-        fillSurfaceRGBA8(mFGSurfaceControl, RED);
-
-        Transaction t;
-        t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-        t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
-        t.show(mBGSurfaceControl);
-
-        t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-        t.show(mFGSurfaceControl);
-
-        // Synchronous transaction will stop this thread, so we set up a
-        // delayed, off-thread vsync request before closing the
-        // transaction. In the test code this is usually done with
-        // TransactionScope. Leaving here in the 'vanilla' form for
-        // reference.
-        ASSERT_EQ(0, sFakeComposer->getFrameCount());
-        sFakeComposer->runVSyncAfter(1ms);
-        t.apply();
-        sFakeComposer->waitUntilFrame(1);
-
-        // Reference data. This is what the HWC should see.
-        static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
-        mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
-        mBaseFrame[BG_LAYER].mSwapCount = 1;
-        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
-        mBaseFrame[FG_LAYER].mSwapCount = 1;
-
-        auto frame = sFakeComposer->getFrameRects(0);
-        ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
-    }
-
-    void TearDown() override {
-        ALOGD("TransactionTest::TearDown");
-
-        mComposerClient->dispose();
-        mBGSurfaceControl = 0;
-        mFGSurfaceControl = 0;
-        mComposerClient = 0;
-
-        sFakeComposer->runVSyncAndWait();
-        mBaseFrame.clear();
-        sFakeComposer->clearFrames();
-        ASSERT_EQ(0, sFakeComposer->getFrameCount());
-
-        sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-        std::vector<gui::LayerDebugInfo> layers;
-        binder::Status status = sf->getLayerDebugInfo(&layers);
-        status_t result = gui::aidl_utils::statusTFromBinderStatus(status);
-        if (result != NO_ERROR) {
-            ALOGE("Failed to get layers %s %d", strerror(-result), result);
-        } else {
-            // If this fails, the test being torn down leaked layers.
-            EXPECT_EQ(0u, layers.size());
-            if (layers.size() > 0) {
-                for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
-                    std::cout << to_string(*layer).c_str();
-                }
-                // To ensure the next test has clean slate, will run the class
-                // tear down and setup here.
-                TearDownTestCase();
-                SetUpTestCase();
-            }
-        }
-        ALOGD("TransactionTest::TearDown - complete");
-    }
-
-    void Test_LayerMove() {
-        ALOGD("TransactionTest::LayerMove");
-
-        // The scope opens and closes a global transaction and, at the
-        // same time, makes sure the SurfaceFlinger progresses one frame
-        // after the transaction closes. The results of the transaction
-        // should be available in the latest frame stored by the fake
-        // composer.
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setPosition(mFGSurfaceControl, 128, 128);
-            // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
-            // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
-            //
-            // sFakeComposer->runVSyncAndWait();
-        }
-
-        fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-        sFakeComposer->runVSyncAndWait();
-
-        ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
-                                                      // there's no extra frames.
-
-        // NOTE: Frame 0 is produced in the SetUp.
-        auto frame1Ref = mBaseFrame;
-        frame1Ref[FG_LAYER].mDisplayFrame =
-                hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
-        EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
-        auto frame2Ref = frame1Ref;
-        frame2Ref[FG_LAYER].mSwapCount++;
-        EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
-    }
-
-    void Test_LayerCrop() {
-        // TODO: Add scaling to confirm that crop happens in buffer space?
-        {
-            TransactionScope ts(*sFakeComposer);
-            Rect cropRect(16, 16, 32, 32);
-            ts.setCrop(mFGSurfaceControl, cropRect);
-        }
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-        auto referenceFrame = mBaseFrame;
-        referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
-        referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetLayer() {
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
-        }
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-        // The layers will switch order, but both are rendered because the background layer is
-        // transparent (RGBA8888).
-        std::vector<RenderState> referenceFrame(2);
-        referenceFrame[0] = mBaseFrame[FG_LAYER];
-        referenceFrame[1] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetLayerOpaque() {
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
-            ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
-                        layer_state_t::eLayerOpaque);
-        }
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-        // The former foreground layer is now covered with opaque layer - it should have disappeared
-        std::vector<RenderState> referenceFrame(1);
-        referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_SetLayerStack() {
-        ALOGD("TransactionTest::SetLayerStack");
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setLayerStack(mFGSurfaceControl, ui::LayerStack{1});
-        }
-
-        // Foreground layer should have disappeared.
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        std::vector<RenderState> refFrame(1);
-        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerShowHide() {
-        ALOGD("TransactionTest::LayerShowHide");
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.hide(mFGSurfaceControl);
-        }
-
-        // Foreground layer should have disappeared.
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        std::vector<RenderState> refFrame(1);
-        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.show(mFGSurfaceControl);
-        }
-
-        // Foreground layer should be back
-        ASSERT_EQ(3, sFakeComposer->getFrameCount());
-        EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetAlpha() {
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setAlpha(mFGSurfaceControl, 0.75f);
-        }
-
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        auto referenceFrame = mBaseFrame;
-        referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetFlags() {
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
-                        layer_state_t::eLayerHidden);
-        }
-
-        // Foreground layer should have disappeared.
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        std::vector<RenderState> refFrame(1);
-        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetMatrix() {
-        struct matrixTestData {
-            float matrix[4];
-            hwc_transform_t expectedTransform;
-            hwc_rect_t expectedDisplayFrame;
-        };
-
-        // The matrix operates on the display frame and is applied before
-        // the position is added. So, the foreground layer rect is (0, 0,
-        // 64, 64) is first transformed, potentially yielding negative
-        // coordinates and then the position (64, 64) is added yielding
-        // the final on-screen rectangles given.
-
-        const matrixTestData MATRIX_TESTS[7] = // clang-format off
-                {{{-1.f, 0.f, 0.f, 1.f},    HWC_TRANSFORM_FLIP_H,           {0, 64, 64, 128}},
-                 {{1.f, 0.f, 0.f, -1.f},    HWC_TRANSFORM_FLIP_V,           {64, 0, 128, 64}},
-                 {{0.f, 1.f, -1.f, 0.f},    HWC_TRANSFORM_ROT_90,           {0, 64, 64, 128}},
-                 {{-1.f, 0.f, 0.f, -1.f},   HWC_TRANSFORM_ROT_180,          {0, 0, 64, 64}},
-                 {{0.f, -1.f, 1.f, 0.f},    HWC_TRANSFORM_ROT_270,          {64, 0, 128, 64}},
-                 {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_H_ROT_90,    {64, 64, 128, 128}},
-                 {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_V_ROT_90,    {64, 64, 128, 128}}};
-        // clang-format on
-        constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
-
-        for (int i = 0; i < TEST_COUNT; i++) {
-            // TODO: How to leverage the HWC2 stringifiers?
-            const matrixTestData& xform = MATRIX_TESTS[i];
-            SCOPED_TRACE(i);
-            {
-                TransactionScope ts(*sFakeComposer);
-                ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1], xform.matrix[2],
-                             xform.matrix[3]);
-            }
-
-            auto referenceFrame = mBaseFrame;
-            referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
-            referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
-
-            EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-        }
-    }
-
-    void Test_SetRelativeLayer() {
-        constexpr int RELATIVE_LAYER = 2;
-        auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64,
-                                                                     64, PIXEL_FORMAT_RGBA_8888, 0);
-        fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
-
-        // Now we stack the surface above the foreground surface and make sure it is visible.
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setPosition(relativeSurfaceControl, 64, 64);
-            ts.show(relativeSurfaceControl);
-            ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl, 1);
-        }
-        auto referenceFrame = mBaseFrame;
-        // NOTE: All three layers will be visible as the surfaces are
-        // transparent because of the RGBA format.
-        referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
-        referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-        // A call to setLayer will override a call to setRelativeLayer
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setLayer(relativeSurfaceControl, 0);
-        }
-
-        // Previous top layer will now appear at the bottom.
-        auto referenceFrame2 = mBaseFrame;
-        referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
-        EXPECT_EQ(3, sFakeComposer->getFrameCount());
-        EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-    }
-
-    sp<SurfaceComposerClient> mComposerClient;
-    sp<SurfaceControl> mBGSurfaceControl;
-    sp<SurfaceControl> mFGSurfaceControl;
-    std::vector<RenderState> mBaseFrame;
-    uint32_t mDisplayWidth;
-    uint32_t mDisplayHeight;
-
-    static inline FakeComposerClient* sFakeComposer;
-};
-
-using TransactionTest_2_1 = TransactionTest<FakeComposerService_2_1>;
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerMove) {
-    Test_LayerMove();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) {
-    Test_LayerCrop();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayer) {
-    Test_LayerSetLayer();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayerOpaque) {
-    Test_LayerSetLayerOpaque();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_SetLayerStack) {
-    Test_SetLayerStack();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerShowHide) {
-    Test_LayerShowHide();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetAlpha) {
-    Test_LayerSetAlpha();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetFlags) {
-    Test_LayerSetFlags();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetMatrix) {
-    Test_LayerSetMatrix();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) {
-    Test_SetRelativeLayer();
-}
-
-template <typename FakeComposerService>
-class ChildLayerTest : public TransactionTest<FakeComposerService> {
-    using Base = TransactionTest<FakeComposerService>;
-
-protected:
-    constexpr static int CHILD_LAYER = 2;
-
-    void SetUp() override {
-        Base::SetUp();
-        mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
-                                                      PIXEL_FORMAT_RGBA_8888, 0,
-                                                      Base::mFGSurfaceControl->getHandle());
-        fillSurfaceRGBA8(mChild, LIGHT_GRAY);
-
-        Base::sFakeComposer->runVSyncAndWait();
-        Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
-        Base::mBaseFrame[CHILD_LAYER].mSwapCount = 1;
-        ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
-        ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void TearDown() override {
-        mChild = 0;
-        Base::TearDown();
-    }
-
-    void Test_Positioning() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(mChild, 10, 10);
-            // Move to the same position as in the original setup.
-            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
-        referenceFrame[CHILD_LAYER].mDisplayFrame =
-                hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-        }
-
-        auto referenceFrame2 = Base::mBaseFrame;
-        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
-        referenceFrame2[CHILD_LAYER].mDisplayFrame =
-                hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
-        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_Cropping() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
-        }
-        // NOTE: The foreground surface would be occluded by the child
-        // now, but is included in the stack because the child is
-        // transparent.
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
-        referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
-        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_Constraints() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setPosition(mChild, 63, 63);
-        }
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
-        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_Scaling() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-        }
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setMatrix(Base::mFGSurfaceControl, 2.0, 0, 0, 2.0);
-        }
-
-        auto referenceFrame2 = Base::mBaseFrame;
-        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
-        referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
-        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerAlpha() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setAlpha(mChild, 0.5);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-        referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setAlpha(Base::mFGSurfaceControl, 0.5);
-        }
-
-        auto referenceFrame2 = referenceFrame;
-        referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
-        referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
-        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    sp<SurfaceControl> mChild;
-};
-
-using ChildLayerTest_2_1 = ChildLayerTest<FakeComposerService_2_1>;
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Positioning) {
-    Test_Positioning();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Cropping) {
-    Test_Cropping();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Constraints) {
-    Test_Constraints();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Scaling) {
-    Test_Scaling();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_LayerAlpha) {
-    Test_LayerAlpha();
-}
-
-template <typename FakeComposerService>
-class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> {
-    using Base = ChildLayerTest<FakeComposerService>;
-
-protected:
-    void SetUp() override {
-        Base::SetUp();
-        Base::mChild =
-                Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
-                                                     PIXEL_FORMAT_RGBA_8888,
-                                                     ISurfaceComposerClient::eFXSurfaceEffect,
-                                                     Base::mFGSurfaceControl->getHandle());
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setColor(Base::mChild,
-                        {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
-            ts.setCrop(Base::mChild, Rect(0, 0, 10, 10));
-        }
-
-        Base::sFakeComposer->runVSyncAndWait();
-        Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
-        Base::mBaseFrame[Base::CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
-        Base::mBaseFrame[Base::CHILD_LAYER].mSwapCount = 0;
-        ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
-        ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerAlpha() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(Base::mChild);
-            ts.setPosition(Base::mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setAlpha(Base::mChild, 0.5);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-        referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setAlpha(Base::mFGSurfaceControl, 0.5);
-        }
-
-        auto referenceFrame2 = referenceFrame;
-        referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
-        referenceFrame2[Base::CHILD_LAYER].mPlaneAlpha = 0.25f;
-        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerZeroAlpha() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(Base::mChild);
-            ts.setPosition(Base::mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setAlpha(Base::mChild, 0.5);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-        referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setAlpha(Base::mFGSurfaceControl, 0.0f);
-        }
-
-        std::vector<RenderState> refFrame(1);
-        refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
-
-        EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-};
-
-using ChildColorLayerTest_2_1 = ChildColorLayerTest<FakeComposerService_2_1>;
-
-TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerAlpha) {
-    Test_LayerAlpha();
-}
-
-TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) {
-    Test_LayerZeroAlpha();
-}
-} // namespace
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-
-    auto* fakeEnvironment = new sftest::FakeHwcEnvironment;
-    ::testing::AddGlobalTestEnvironment(fakeEnvironment);
-    ::testing::InitGoogleMock(&argc, argv);
-    return RUN_ALL_TESTS();
-}
-
-// 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/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
index 69d30c4..513f779 100644
--- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
+++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
@@ -50,10 +50,8 @@
     sp<NiceMock<MockIPower>> mMockHal = nullptr;
     sp<NiceMock<MockIPowerHintSession>> mMockSession = nullptr;
     void verifyAndClearExpectations();
-    void sendActualWorkDurationGroup(std::vector<WorkDuration> durations,
-                                     std::chrono::nanoseconds sleepBeforeLastSend);
-    std::chrono::nanoseconds mAllowedDeviation;
-    std::chrono::nanoseconds mStaleTimeout;
+    void sendActualWorkDurationGroup(std::vector<WorkDuration> durations);
+    static constexpr std::chrono::duration kStaleTimeout = 100ms;
 };
 
 void AidlPowerHalWrapperTest::SetUp() {
@@ -61,9 +59,6 @@
     mMockSession = sp<NiceMock<MockIPowerHintSession>>::make();
     ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok()));
     mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
-    mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count());
-    mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation};
-    mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout;
 }
 
 void AidlPowerHalWrapperTest::verifyAndClearExpectations() {
@@ -71,14 +66,11 @@
     Mock::VerifyAndClearExpectations(mMockSession.get());
 }
 
-void AidlPowerHalWrapperTest::sendActualWorkDurationGroup(
-        std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend) {
+void AidlPowerHalWrapperTest::sendActualWorkDurationGroup(std::vector<WorkDuration> durations) {
     for (size_t i = 0; i < durations.size(); i++) {
-        if (i == durations.size() - 1) {
-            std::this_thread::sleep_for(sleepBeforeLastSend);
-        }
         auto duration = durations[i];
-        mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos);
+        mWrapper->sendActualWorkDuration(Duration::fromNs(duration.durationNanos),
+                                         TimePoint::fromNs(duration.timeStampNanos));
     }
 }
 
@@ -164,13 +156,13 @@
 
     for (const auto& test : testCases) {
         // reset to 100ms baseline
-        mWrapper->setTargetWorkDuration(1);
-        mWrapper->setTargetWorkDuration(base.count());
+        mWrapper->setTargetWorkDuration(1ns);
+        mWrapper->setTargetWorkDuration(base);
 
-        auto target = test.first;
+        std::chrono::nanoseconds target = test.first;
         EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count()))
                 .Times(test.second ? 1 : 0);
-        mWrapper->setTargetWorkDuration(target.count());
+        mWrapper->setTargetWorkDuration(target);
         verifyAndClearExpectations();
     }
 }
@@ -187,7 +179,7 @@
 
     EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1))
             .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
-    mWrapper->setTargetWorkDuration(1);
+    mWrapper->setTargetWorkDuration(1ns);
     EXPECT_TRUE(mWrapper->shouldReconnectHAL());
 }
 
@@ -206,58 +198,24 @@
     // 100ms
     const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
             testCases = {{{{-1ms, 100}}, false},
-                         {{{100ms - (mAllowedDeviation / 2), 100}}, false},
-                         {{{100ms + (mAllowedDeviation / 2), 100}}, false},
-                         {{{100ms + (mAllowedDeviation + 1ms), 100}}, true},
-                         {{{100ms - (mAllowedDeviation + 1ms), 100}}, true},
+                         {{{50ms, 100}}, true},
                          {{{100ms, 100}, {200ms, 200}}, true},
                          {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}};
 
     for (const auto& test : testCases) {
         // reset actual duration
-        sendActualWorkDurationGroup({base}, mStaleTimeout);
+        sendActualWorkDurationGroup({base});
 
         auto raw = test.first;
         std::vector<WorkDuration> durations(raw.size());
         std::transform(raw.begin(), raw.end(), durations.begin(),
                        [](auto d) { return toWorkDuration(d); });
-        EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
-                .Times(test.second ? 1 : 0);
-        sendActualWorkDurationGroup(durations, 0ms);
-        verifyAndClearExpectations();
-    }
-}
-
-TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
-    std::vector<int32_t> threadIds = {1, 2};
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    ASSERT_TRUE(mWrapper->startPowerHintSession());
-    verifyAndClearExpectations();
-
-    auto base = toWorkDuration(100ms, 0);
-    // test cases with actual work durations and whether it should update hint against baseline
-    // 100ms
-    const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>,
-                                 std::chrono::nanoseconds, bool>>
-            testCases = {{{{100ms, 100}}, mStaleTimeout, true},
-                         {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true},
-                         {{{100ms, 100}}, mStaleTimeout / 2, false}};
-
-    for (const auto& test : testCases) {
-        // reset actual duration
-        sendActualWorkDurationGroup({base}, mStaleTimeout);
-
-        auto raw = std::get<0>(test);
-        std::vector<WorkDuration> durations(raw.size());
-        std::transform(raw.begin(), raw.end(), durations.begin(),
-                       [](auto d) { return toWorkDuration(d); });
-        EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
-                .Times(std::get<2>(test) ? 1 : 0);
-        sendActualWorkDurationGroup(durations, std::get<1>(test));
+        for (auto& duration : durations) {
+            EXPECT_CALL(*mMockSession.get(),
+                        reportActualWorkDuration(std::vector<WorkDuration>{duration}))
+                    .Times(test.second ? 1 : 0);
+        }
+        sendActualWorkDurationGroup(durations);
         verifyAndClearExpectations();
     }
 }
@@ -275,7 +233,7 @@
     duration.durationNanos = 1;
     EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_))
             .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
-    sendActualWorkDurationGroup({duration}, 0ms);
+    sendActualWorkDurationGroup({duration});
     EXPECT_TRUE(mWrapper->shouldReconnectHAL());
 }
 
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7823363..d2b5813 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -24,6 +24,7 @@
 filegroup {
     name: "libsurfaceflinger_mock_sources",
     srcs: [
+        "mock/DisplayHardware/MockAidlPowerHalWrapper.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockHWC2.cpp",
         "mock/DisplayHardware/MockIPower.cpp",
@@ -33,7 +34,6 @@
         "mock/MockFrameTimeline.cpp",
         "mock/MockFrameTracer.cpp",
         "mock/MockNativeWindowSurface.cpp",
-        "mock/MockSurfaceInterceptor.cpp",
         "mock/MockTimeStats.cpp",
         "mock/MockVsyncController.cpp",
         "mock/MockVSyncTracker.cpp",
@@ -95,6 +95,7 @@
         "LayerTest.cpp",
         "LayerTestUtils.cpp",
         "MessageQueueTest.cpp",
+        "PowerAdvisorTest.cpp",
         "SurfaceFlinger_CreateDisplayTest.cpp",
         "SurfaceFlinger_DestroyDisplayTest.cpp",
         "SurfaceFlinger_DisplayModeSwitching.cpp",
@@ -107,6 +108,7 @@
         "SurfaceFlinger_SetDisplayStateTest.cpp",
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
         "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
+        "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
         "SchedulerTest.cpp",
         "SetFrameRateTest.cpp",
         "RefreshRateConfigsTest.cpp",
@@ -133,15 +135,17 @@
 
 cc_defaults {
     name: "libsurfaceflinger_mocks_defaults",
+    defaults: [
+        "android.hardware.graphics.common-ndk_static",
+        "android.hardware.graphics.composer3-ndk_static",
+    ],
     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",
@@ -163,7 +167,6 @@
         "libtimestats_atoms_proto",
         "libtimestats_proto",
         "libtonemap",
-        "libtrace_proto",
         "perfetto_trace_protos",
     ],
     shared_libs: [
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 9082a22..c1cbbfb 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -20,7 +20,8 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <gui/BufferQueue.h>
-#include "BufferStateLayer.h"
+
+#include "HwcSlotGenerator.h"
 
 namespace android {
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 2571e3a..7148c11 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -38,7 +38,6 @@
 #include <utils/String8.h>
 
 #include "DisplayRenderArea.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -309,16 +308,20 @@
                 compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
                                                        ceDisplayArgs);
 
+        constexpr auto kDisplayConnectionType = ui::DisplayConnectionType::Internal;
+        constexpr bool kIsPrimary = true;
+
         test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
-                                                   ui::DisplayConnectionType::Internal, HWC_DISPLAY,
-                                                   true /* isPrimary */)
+                                                   kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
                                  .setDisplaySurface(test->mDisplaySurface)
                                  .setNativeWindow(test->mNativeWindow)
                                  .setSecure(Derived::IS_SECURE)
                                  .setPowerMode(Derived::INIT_POWER_MODE)
                                  .inject();
         Mock::VerifyAndClear(test->mNativeWindow.get());
-        test->mDisplay->setLayerStack(LAYER_STACK);
+
+        constexpr bool kIsInternal = kDisplayConnectionType == ui::DisplayConnectionType::Internal;
+        test->mDisplay->setLayerFilter({LAYER_STACK, kIsInternal});
     }
 
     template <typename Case>
@@ -351,15 +354,13 @@
                 .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> {
+                                    const bool, base::unique_fd&&) -> std::future<FenceResult> {
                     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 futureOf<renderengine::RenderEngineResult>(
-                            {NO_ERROR, base::unique_fd()});
+                    return futureOf<FenceResult>(Fence::NO_FENCE);
                 });
     }
 
@@ -404,16 +405,14 @@
                 .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> {
+                                    const bool, base::unique_fd&&) -> std::future<FenceResult> {
                     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 futureOf<renderengine::RenderEngineResult>(
-                            {NO_ERROR, base::unique_fd()});
+                    return futureOf<FenceResult>(Fence::NO_FENCE);
                 });
     }
 
@@ -492,7 +491,7 @@
     static constexpr IComposerClient::BlendMode BLENDMODE =
             IComposerClient::BlendMode::PREMULTIPLIED;
 
-    static void setupLatchedBuffer(CompositionTest* test, sp<BufferStateLayer> layer) {
+    static void setupLatchedBuffer(CompositionTest* test, sp<Layer> layer) {
         Mock::VerifyAndClear(test->mRenderEngine);
 
         const auto buffer = std::make_shared<
@@ -516,7 +515,7 @@
         Mock::VerifyAndClear(test->mRenderEngine);
     }
 
-    static void setupLayerState(CompositionTest* test, sp<BufferStateLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         setupLatchedBuffer(test, layer);
     }
 
@@ -606,7 +605,7 @@
                 .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> {
+                              base::unique_fd&&) -> std::future<FenceResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -614,9 +613,7 @@
                               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()});
+                    std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE);
                     if (layerSettings.empty()) {
                         ADD_FAILURE() << "layerSettings was not expected to be empty in "
                                          "setupREBufferCompositionCommonCallExpectations "
@@ -659,7 +656,7 @@
                 .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> {
+                              base::unique_fd&&) -> std::future<FenceResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -667,9 +664,7 @@
                               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()});
+                    std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE);
                     if (layerSettings.empty()) {
                         ADD_FAILURE()
                                 << "layerSettings was not expected to be empty in "
@@ -704,7 +699,7 @@
     using Base = BaseLayerProperties<SidebandLayerProperties>;
     static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
 
-    static void setupLayerState(CompositionTest* test, sp<BufferStateLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         sp<NativeHandle> stream =
                 NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                      false);
@@ -740,7 +735,7 @@
                 .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> {
+                              base::unique_fd&&) -> std::future<FenceResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -748,9 +743,7 @@
                               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()});
+                    std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE);
                     if (layerSettings.empty()) {
                         ADD_FAILURE() << "layerSettings was not expected to be empty in "
                                          "setupInsecureREBufferCompositionCommonCallExpectations "
@@ -787,14 +780,14 @@
 struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
     using Base = BaseLayerProperties<CursorLayerProperties>;
 
-    static void setupLayerState(CompositionTest* test, sp<BufferStateLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         Base::setupLayerState(test, layer);
         test->mFlinger.setLayerPotentialCursor(layer, true);
     }
 };
 
 struct NoLayerVariant {
-    using FlingerLayerType = sp<BufferStateLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); }
     static void injectLayer(CompositionTest*, FlingerLayerType) {}
@@ -828,8 +821,6 @@
     static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
         layerDrawingState.layerStack = LAYER_STACK;
-        layerDrawingState.width = 100;
-        layerDrawingState.height = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
         layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
@@ -865,13 +856,13 @@
 template <typename LayerProperties>
 struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<EffectLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
-        FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
-            return sp<EffectLayer>::make(
-                    LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
-                                      LayerProperties::LAYER_FLAGS, LayerMetadata()));
+        FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
+            return sp<Layer>::make(LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(),
+                                                     "test-layer", LayerProperties::LAYER_FLAGS,
+                                                     LayerMetadata()));
         });
 
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
@@ -901,17 +892,17 @@
 template <typename LayerProperties>
 struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<BufferStateLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
         test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID);
 
         FlingerLayerType layer =
-                Base::template createLayerWithFactory<BufferStateLayer>(test, [test]() {
+                Base::template createLayerWithFactory<Layer>(test, [test]() {
                     LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
                                            LayerProperties::LAYER_FLAGS, LayerMetadata());
                     args.textureName = test->mFlinger.mutableTexturePool().back();
-                    return sp<BufferStateLayer>::make(args);
+                    return sp<Layer>::make(args);
                 });
 
         LayerProperties::setupLayerState(test, layer);
@@ -953,12 +944,12 @@
 template <typename LayerProperties>
 struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<EffectLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
         LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
                                LayerProperties::LAYER_FLAGS, LayerMetadata());
-        FlingerLayerType layer = sp<EffectLayer>::make(args);
+        FlingerLayerType layer = sp<Layer>::make(args);
         Base::template initLayerDrawingStateAndComputeBounds(test, layer);
         return layer;
     }
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index ec27eda..67ace1a 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -292,9 +292,10 @@
 
 TEST_F(DispSyncSourceTest, getLatestVsyncData) {
     const nsecs_t now = systemTime();
-    const nsecs_t vsyncInternalDuration = mWorkDuration.count() + mReadyDuration.count();
+    const nsecs_t expectedPresentationTime =
+            now + mWorkDuration.count() + mReadyDuration.count() + 1;
     EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
-            .WillOnce(Return(now + vsyncInternalDuration + 1));
+            .WillOnce(Return(expectedPresentationTime));
     {
         InSequence seq;
         EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
@@ -306,10 +307,8 @@
     EXPECT_TRUE(mDispSyncSource);
 
     const auto vsyncData = mDispSyncSource->getLatestVSyncData();
-    ASSERT_GT(vsyncData.deadlineTimestamp, now);
-    ASSERT_GT(vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp);
-    EXPECT_EQ(vsyncData.deadlineTimestamp,
-              vsyncData.expectedPresentationTime - vsyncInternalDuration);
+    ASSERT_EQ(vsyncData.expectedPresentationTime, expectedPresentationTime);
+    EXPECT_EQ(vsyncData.deadlineTimestamp, expectedPresentationTime - mReadyDuration.count());
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 93af225..982b9ff 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -42,6 +42,7 @@
         PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
 
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+        mFlinger.configureAndCommit();
 
         mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
                            .setDisplayModes(makeModes(kMode60, kMode90, kMode120), kModeId60)
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 044c112..8b91c67 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -36,8 +36,7 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    // Default to no wide color display support configured
-    mFlinger.mutableHasWideColorDisplay() = false;
+    mFlinger.mutableSupportsWideColor() = false;
     mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
 
     mFlinger.setCreateBufferQueueFunction([](auto, auto, auto) {
@@ -51,7 +50,6 @@
 
     injectMockScheduler();
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
-    mFlinger.mutableInterceptor() = mSurfaceInterceptor;
 
     injectMockComposer(0);
 }
@@ -168,7 +166,12 @@
 }
 
 bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) const {
-    return mFlinger.hwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
+    const auto& map = mFlinger.hwcPhysicalDisplayIdMap();
+
+    const auto it = map.find(hwcDisplayId);
+    if (it == map.end()) return false;
+
+    return mFlinger.hwcDisplayData().count(it->second) == 1;
 }
 
 bool DisplayTransactionTest::hasTransactionFlagSet(int32_t flag) const {
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 0c071d4..9cceb5e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -49,7 +49,6 @@
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSurfaceInterceptor.h"
 #include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
 
@@ -120,7 +119,6 @@
     // 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;
-    sp<mock::SurfaceInterceptor> mSurfaceInterceptor = sp<mock::SurfaceInterceptor>::make();
 
     mock::VsyncController* mVsyncController = new mock::VsyncController;
     mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
@@ -434,14 +432,18 @@
         }
     }
 
+    template <bool kFailedHotplug = false>
     static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
-        constexpr auto CONNECTION_TYPE =
-                PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal
-                ? IComposerClient::DisplayConnectionType::INTERNAL
-                : IComposerClient::DisplayConnectionType::EXTERNAL;
+        if constexpr (!kFailedHotplug) {
+            constexpr auto CONNECTION_TYPE =
+                    PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal
+                    ? IComposerClient::DisplayConnectionType::INTERNAL
+                    : IComposerClient::DisplayConnectionType::EXTERNAL;
 
-        EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
+            EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
+                    .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE),
+                                    Return(hal::V2_4::Error::NONE)));
+        }
 
         EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
                 .WillOnce(Return(hal::Error::NONE));
@@ -675,12 +677,10 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = false;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableHasWideColorDisplay() = true;
+        test->mFlinger.mutableSupportsWideColor() = true;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
         EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
     }
 };
@@ -692,12 +692,11 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = false;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableHasWideColorDisplay() = false;
+        test->mFlinger.mutableSupportsWideColor() = false;
         test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
         EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
         EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
     }
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 978afc5..a5beaba 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -93,7 +93,6 @@
     void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
                                             std::chrono::nanoseconds expectedReadyDuration);
     VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
-    void expectInterceptCallReceived(nsecs_t expectedTimestamp);
     void expectVsyncEventReceivedByConnection(const char* name,
                                               ConnectionEventRecorder& connectionEventRecorder,
                                               nsecs_t expectedTimestamp, unsigned expectedCount);
@@ -114,7 +113,6 @@
     AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)>
             mVSyncSetDurationCallRecorder;
     AsyncCallRecorder<void (*)()> mResyncCallRecorder;
-    AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
     AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
     ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
@@ -181,7 +179,6 @@
 
     mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
     mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
-                                                  mInterceptVSyncCallRecorder.getInvocable(),
                                                   throttleVsync, getVsyncPeriod);
 
     // EventThread should register itself as VSyncSource callback.
@@ -219,12 +216,6 @@
     return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
 }
 
-void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) {
-    auto args = mInterceptVSyncCallRecorder.waitForCall();
-    ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
-}
-
 void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
     auto args = mThrottleVsyncCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
@@ -348,7 +339,6 @@
     EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
-    EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
 }
 
@@ -374,17 +364,15 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Use the received callback to signal a first vsync event.
-    // The interceptor should receive the event, as well as the connection.
+    // The throttler should receive the event, as well as the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // Use the received callback to signal a second vsync event.
-    // The interceptor should receive the event, but the connection should
+    // The throttler should receive the event, but the connection should
     // not as it was only interested in the first.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -400,10 +388,9 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Use the received callback to signal a vsync event.
-    // The interceptor should receive the event, as well as the connection.
+    // The throttler should receive the event, as well as the connection.
     VSyncSource::VSyncData vsyncData = {456, 789};
     mCallback->onVSyncEvent(123, vsyncData);
-    expectInterceptCallReceived(123);
     expectVsyncEventFrameTimelinesCorrect(123, vsyncData);
 }
 
@@ -477,10 +464,9 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Send a vsync event. EventThread should then make a call to the
-    // interceptor, and the second connection. The first connection should not
+    // the second connection. The first connection should not
     // get the event.
     mCallback->onVSyncEvent(123, {456, 0});
-    expectInterceptCallReceived(123);
     EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
     expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
                                          1u);
@@ -493,21 +479,18 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Send a vsync event. EventThread should then make a call to the
-    // interceptor, and the connection.
+    // throttler, and the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     expectThrottleVsyncReceived(123, mConnectionUid);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
     mCallback->onVSyncEvent(789, {777, 111});
-    expectInterceptCallReceived(789);
     expectThrottleVsyncReceived(777, mConnectionUid);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
@@ -518,27 +501,23 @@
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
-    // The first event will be seen by the interceptor, and not the connection.
+    // The first event will not be seen by the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The second event will be seen by the interceptor and the connection.
+    // The second event will be seen by the connection.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The third event will be seen by the interceptor, and not the connection.
+    // The third event will not be seen by the connection.
     mCallback->onVSyncEvent(789, {777, 744});
-    expectInterceptCallReceived(789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The fourth event will be seen by the interceptor and the connection.
+    // The fourth event will be seen by the connection.
     mCallback->onVSyncEvent(101112, {7847, 86});
-    expectInterceptCallReceived(101112);
     expectVsyncEventReceivedByConnection(101112, 4u);
 }
 
@@ -551,9 +530,8 @@
     // Destroy the only (strong) reference to the connection.
     mConnection = nullptr;
 
-    // The first event will be seen by the interceptor, and not the connection.
+    // The first event will not be seen by the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should disable vsync callbacks
@@ -568,16 +546,12 @@
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an error.
+    // The first event will be seen by the connection, which then returns an error.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
-    // A subsequent event will be seen by the interceptor and not by the
-    // connection.
+    // A subsequent event will not be seen by the connection.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should disable vsync callbacks with the second event
@@ -599,10 +573,8 @@
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an error.
+    // The first event will be seen by the connection, which then returns an error.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
     expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
                                          1u);
@@ -617,16 +589,13 @@
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an non-fatal error.
+    // The first event will be seen by the connection, which then returns a non-fatal error.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
-    // A subsequent event will be seen by the interceptor, and by the connection,
-    // which still then returns an non-fatal error.
+    // A subsequent event will be seen by the connection, which still then returns a non-fatal
+    // error.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
 
     // EventThread will not disable vsync callbacks as the errors are non-fatal.
@@ -748,17 +717,15 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Use the received callback to signal a first vsync event.
-    // The interceptor should receive the event, but not the connection.
+    // The throttler should receive the event, but not the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectThrottleVsyncReceived(456, mThrottledConnectionUid);
     mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
 
     // Use the received callback to signal a second vsync event.
-    // The interceptor should receive the event, but the connection should
+    // The throttler should receive the event, but the connection should
     // not as it was only interested in the first.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     expectThrottleVsyncReceived(123, mThrottledConnectionUid);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
new file mode 100644
index 0000000..81b420c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::hardware::graphics::composer::hal::HWDisplayId;
+using android::Hwc2::mock::PowerAdvisor;
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Mock;
+using testing::ResultOf;
+using testing::Return;
+using testing::SetArgPointee;
+
+class FakeDisplayInjector {
+public:
+    sp<DisplayDevice> injectDefaultInternalDisplay(
+            const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra,
+            TestableSurfaceFlinger& flinger, uint8_t port = 255u) const {
+        constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
+        constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
+        constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
+
+        const PhysicalDisplayId physicalDisplayId = PhysicalDisplayId::fromPort(port);
+
+        // The DisplayDevice is required to have a framebuffer (behind the
+        // ANativeWindow interface) which uses the actual hardware display
+        // size.
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+        auto compositionDisplay = compositionengine::impl::
+                createDisplay(flinger.getCompositionEngine(),
+                              compositionengine::DisplayCreationArgsBuilder()
+                                      .setId(physicalDisplayId)
+                                      .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                                      .setPowerAdvisor(mPowerAdvisor)
+                                      .build());
+
+        constexpr bool kIsPrimary = true;
+        auto injector = FakeDisplayDeviceInjector(flinger, compositionDisplay,
+                                                  ui::DisplayConnectionType::Internal,
+                                                  DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
+
+        injector.setNativeWindow(mNativeWindow);
+        if (injectExtra) {
+            injectExtra(injector);
+        }
+
+        auto displayDevice = injector.inject();
+
+        Mock::VerifyAndClear(mNativeWindow.get());
+
+        return displayDevice;
+    }
+
+    sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+    PowerAdvisor* mPowerAdvisor = new PowerAdvisor();
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index 0b4e196..1cd9e49 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -24,8 +24,6 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "FpsReporter.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
@@ -80,7 +78,7 @@
     static constexpr int32_t PRIORITY_UNSET = -1;
 
     void setupScheduler();
-    sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
+    sp<Layer> createBufferStateLayer(LayerMetadata metadata);
 
     TestableSurfaceFlinger mFlinger;
     mock::FrameTimeline mFrameTimeline =
@@ -116,10 +114,10 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
+sp<Layer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
-    return sp<BufferStateLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 void FpsReporterTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 874fa7c..f47ac6d 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -480,7 +480,7 @@
     addEmptyDisplayFrame();
 
     auto displayFrame0 = getDisplayFrame(0);
-    EXPECT_EQ(displayFrame0->getActuals().presentTime, -1);
+    EXPECT_EQ(displayFrame0->getActuals().presentTime, 59);
     EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown);
     EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1);
     EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
@@ -2228,6 +2228,50 @@
     EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
 }
 
+TEST_F(FrameTimelineTest, jankClassification_presentFenceError) {
+    auto erroneousPresentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto erroneousPresentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto validPresentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    int64_t sfToken3 = mTokenManager->generateTokenForPredictions({72, 80, 80});
+
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfPresent(26, erroneousPresentFence1);
+
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfPresent(60, erroneousPresentFence2);
+
+    mFrameTimeline->setSfWakeUp(sfToken3, 72, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfPresent(80, validPresentFence);
+
+    validPresentFence->signalForTest(80);
+
+    addEmptyDisplayFrame();
+
+    {
+        auto displayFrame = getDisplayFrame(0);
+        EXPECT_EQ(displayFrame->getActuals().presentTime, 26);
+        EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
+        EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
+        EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+    }
+    {
+        auto displayFrame = getDisplayFrame(1);
+        EXPECT_EQ(displayFrame->getActuals().presentTime, 60);
+        EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
+        EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
+        EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+    }
+    {
+        auto displayFrame = getDisplayFrame(2);
+        EXPECT_EQ(displayFrame->getActuals().presentTime, 80);
+        EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+        EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+        EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+    }
+}
+
 TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) {
     EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f);
 }
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
index cd857c3..29aa717 100644
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -53,11 +53,10 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
-                               LayerMetadata());
-        return sp<BufferStateLayer>::make(args);
+        LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata());
+        return sp<Layer>::make(args);
     }
 
     void setupScheduler() {
@@ -108,9 +107,9 @@
 };
 
 TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer1 = createLayer();
+    sp<Layer> childLayer2 = createLayer();
     rootLayer->addChild(childLayer1);
     rootLayer->addChild(childLayer2);
     rootLayer->setGameModeForTree(GameMode::Performance);
@@ -121,8 +120,8 @@
 }
 
 TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer = createLayer();
     rootLayer->setGameModeForTree(GameMode::Performance);
     rootLayer->addChild(childLayer);
 
@@ -131,8 +130,8 @@
 }
 
 TEST_F(GameModeTest, RemoveChildResetsGameMode) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer = createLayer();
     rootLayer->setGameModeForTree(GameMode::Performance);
     rootLayer->addChild(childLayer);
 
@@ -144,9 +143,9 @@
 }
 
 TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer1 = createLayer();
+    sp<Layer> childLayer2 = createLayer();
     rootLayer->setGameModeForTree(GameMode::Standard);
     rootLayer->addChild(childLayer1);
 
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 5241604..9d8e0a2 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -59,27 +59,62 @@
 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());
+struct HWComposerTest : testing::Test {
+    using HalError = hardware::graphics::composer::V2_1::Error;
 
-    const hal::HWDisplayId hwcId = 1;
+    Hwc2::mock::Composer* const mHal = new StrictMock<Hwc2::mock::Composer>();
+    impl::HWComposer mHwc{std::unique_ptr<Hwc2::Composer>(mHal)};
 
-    EXPECT_CALL(*mHal, getDisplayIdentificationData(_, _, _))
-            .WillOnce(DoAll(SetArgPointee<2>(getExternalEdid()),
-                            Return(hardware::graphics::composer::V2_1::Error::NONE)));
+    void expectHotplugConnect(hal::HWDisplayId hwcDisplayId) {
+        constexpr uint8_t kPort = 255;
+        EXPECT_CALL(*mHal, getDisplayIdentificationData(hwcDisplayId, _, _))
+                .WillOnce(DoAll(SetArgPointee<1>(kPort),
+                                SetArgPointee<2>(getExternalEdid()), Return(HalError::NONE)));
 
-    EXPECT_CALL(*mHal, setVsyncEnabled(_, _));
-    EXPECT_CALL(*mHal, setClientTargetSlotCount(_));
+        EXPECT_CALL(*mHal, setClientTargetSlotCount(_));
+        EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE));
+    }
+};
 
-    auto info = hwc.onHotplug(hwcId, hal::Connection::CONNECTED);
+TEST_F(HWComposerTest, isHeadless) {
+    ASSERT_TRUE(mHwc.isHeadless());
+
+    constexpr hal::HWDisplayId kHwcDisplayId = 1;
+    expectHotplugConnect(kHwcDisplayId);
+
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
     ASSERT_TRUE(info);
-    auto displayId = info->id;
-    ASSERT_FALSE(hwc.isHeadless());
 
-    hwc.disconnectDisplay(displayId);
-    ASSERT_TRUE(hwc.isHeadless());
+    ASSERT_FALSE(mHwc.isHeadless());
+
+    mHwc.disconnectDisplay(info->id);
+    ASSERT_TRUE(mHwc.isHeadless());
+}
+
+TEST_F(HWComposerTest, getActiveMode) {
+    // Unknown display.
+    EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), std::nullopt);
+
+    constexpr hal::HWDisplayId kHwcDisplayId = 2;
+    expectHotplugConnect(kHwcDisplayId);
+
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+    ASSERT_TRUE(info);
+
+    {
+        // Display is known to SF but not HWC, e.g. the hotplug disconnect is pending.
+        EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _))
+                .WillOnce(Return(HalError::BAD_DISPLAY));
+
+        EXPECT_EQ(mHwc.getActiveMode(info->id), std::nullopt);
+    }
+    {
+        constexpr hal::HWConfigId kConfigId = 42;
+        EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _))
+                .WillOnce(DoAll(SetArgPointee<1>(kConfigId), Return(HalError::NONE)));
+
+        EXPECT_EQ(mHwc.getActiveMode(info->id), kConfigId);
+    }
 }
 
 struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
@@ -93,8 +128,7 @@
     MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId));
 };
 
-struct HWComposerSetCallbackTest : testing::Test {
-    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+struct HWComposerSetCallbackTest : HWComposerTest {
     MockHWC2ComposerCallback mCallback;
 };
 
@@ -113,10 +147,9 @@
                             Return(hardware::graphics::composer::V2_4::Error::NONE)));
     EXPECT_CALL(*mHal, registerCallback(_));
 
-    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
-    hwc.setCallback(mCallback);
+    mHwc.setCallback(mCallback);
 
-    const auto& supported = hwc.getSupportedLayerGenericMetadata();
+    const auto& supported = mHwc.getSupportedLayerGenericMetadata();
     EXPECT_EQ(2u, supported.size());
     EXPECT_EQ(1u, supported.count(kMetadata1Name));
     EXPECT_EQ(kMetadata1Mandatory, supported.find(kMetadata1Name)->second);
@@ -130,11 +163,10 @@
             .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
     EXPECT_CALL(*mHal, registerCallback(_));
 
-    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
-    hwc.setCallback(mCallback);
+    mHwc.setCallback(mCallback);
 
-    const auto& supported = hwc.getSupportedLayerGenericMetadata();
-    EXPECT_EQ(0u, supported.size());
+    const auto& supported = mHwc.getSupportedLayerGenericMetadata();
+    EXPECT_TRUE(supported.empty());
 }
 
 struct HWComposerLayerTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp
index 4974f90..95e54f6 100644
--- a/services/surfaceflinger/tests/unittests/LayerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerTest.cpp
@@ -17,7 +17,6 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
-#include <EffectLayer.h>
 #include <gtest/gtest.h>
 #include <ui/FloatRect.h>
 #include <ui/Transform.h>
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
index b7a8a93..ee42e19 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
@@ -29,13 +29,13 @@
     sp<Client> client;
     LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
                            LayerMetadata());
-    return sp<BufferStateLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 sp<Layer> EffectLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
     sp<Client> client;
     LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
-    return sp<EffectLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 std::string PrintToStringParamName(
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
index fc9b6a2..ab446fa 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.h
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
@@ -23,8 +23,6 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
-#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"
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index f20b9f9..5e1042e 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -32,6 +32,7 @@
 using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
 
 struct NoOpCompositor final : ICompositor {
+    void configure() override {}
     bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
     void composite(TimePoint, VsyncId) override {}
     void sample() override {}
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
new file mode 100644
index 0000000..2d66d3c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "PowerAdvisorTest"
+
+#include <DisplayHardware/PowerAdvisor.h>
+#include <compositionengine/Display.h>
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/DisplayId.h>
+#include <chrono>
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockAidlPowerHalWrapper.h"
+
+using namespace android;
+using namespace android::Hwc2::mock;
+using namespace android::hardware::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+namespace android::Hwc2::impl {
+
+class PowerAdvisorTest : public testing::Test {
+public:
+    void SetUp() override;
+    void startPowerHintSession();
+    void fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod);
+    void setExpectedTiming(Duration totalFrameTargetDuration, TimePoint expectedPresentTime);
+    Duration getFenceWaitDelayDuration(bool skipValidate);
+
+protected:
+    TestableSurfaceFlinger mFlinger;
+    std::unique_ptr<PowerAdvisor> mPowerAdvisor;
+    NiceMock<MockAidlPowerHalWrapper>* mMockAidlWrapper;
+    Duration kErrorMargin = 1ms;
+};
+
+void PowerAdvisorTest::SetUp() FTL_FAKE_GUARD(mPowerAdvisor->mPowerHalMutex) {
+    std::unique_ptr<MockAidlPowerHalWrapper> mockAidlWrapper =
+            std::make_unique<NiceMock<MockAidlPowerHalWrapper>>();
+    mPowerAdvisor = std::make_unique<PowerAdvisor>(*mFlinger.flinger());
+    ON_CALL(*mockAidlWrapper.get(), supportsPowerHintSession()).WillByDefault(Return(true));
+    ON_CALL(*mockAidlWrapper.get(), startPowerHintSession()).WillByDefault(Return(true));
+    mPowerAdvisor->mHalWrapper = std::move(mockAidlWrapper);
+    mMockAidlWrapper =
+            reinterpret_cast<NiceMock<MockAidlPowerHalWrapper>*>(mPowerAdvisor->mHalWrapper.get());
+}
+
+void PowerAdvisorTest::startPowerHintSession() {
+    const std::vector<int32_t> threadIds = {1, 2, 3};
+    mPowerAdvisor->enablePowerHint(true);
+    mPowerAdvisor->startPowerHintSession(threadIds);
+}
+
+void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration,
+                                         TimePoint expectedPresentTime) {
+    mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTargetDuration);
+    mPowerAdvisor->setExpectedPresentTime(expectedPresentTime);
+}
+
+void PowerAdvisorTest::fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod) {
+    mPowerAdvisor->setCommitStart(startTime);
+    mPowerAdvisor->setFrameDelay(0ns);
+    mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+}
+
+Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
+    return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
+                         : PowerAdvisor::kFenceWaitStartDelayValidated);
+}
+
+namespace {
+
+TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+    // 60hz
+    const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+    const Duration presentDuration = 5ms;
+    const Duration postCompDuration = 1ms;
+
+    TimePoint startTime{100ns};
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const Duration expectedDuration = kErrorMargin + presentDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+    // 60hz
+    const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+    const Duration presentDuration = 5ms;
+    const Duration postCompDuration = 1ms;
+    const Duration hwcBlockedDuration = 500us;
+
+    TimePoint startTime{100ns};
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const Duration expectedDuration = kErrorMargin + presentDuration +
+            getFenceWaitDelayDuration(false) - hwcBlockedDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 3ms);
+    // now report the fence as having fired during the display HWC time
+    mPowerAdvisor->setSfPresentTiming(startTime + 2ms + hwcBlockedDuration,
+                                      startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) {
+    mPowerAdvisor->onBootFinished();
+    startPowerHintSession();
+
+    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0),
+                                      GpuVirtualDisplayId(1)};
+
+    // 60hz
+    const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+    // make present duration much later than the hwc display by itself will account for
+    const Duration presentDuration{10ms};
+    const Duration postCompDuration{1ms};
+
+    TimePoint startTime{100ns};
+
+    // advisor only starts on frame 2 so do an initial no-op frame
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+    // increment the frame
+    startTime += vsyncPeriod;
+
+    const Duration expectedDuration = kErrorMargin + presentDuration + postCompDuration;
+    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+
+    fakeBasicFrameTiming(startTime, vsyncPeriod);
+    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+    mPowerAdvisor->setDisplays(displayIds);
+
+    // don't report timing for the gpu displays since they don't use hwc
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
+    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+    mPowerAdvisor->sendActualWorkDuration();
+}
+
+} // namespace
+} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index fcde532..620825f 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "SchedulerUnittests"
 
 #include <ftl/enum.h>
+#include <ftl/fake_guard.h>
 #include <gmock/gmock.h>
 #include <log/log.h>
 #include <ui/Size.h>
@@ -33,6 +34,7 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
+using SetPolicyResult = RefreshRateConfigs::SetPolicyResult;
 using LayerVoteType = RefreshRateConfigs::LayerVoteType;
 using LayerRequirement = RefreshRateConfigs::LayerRequirement;
 
@@ -40,6 +42,17 @@
 
 struct TestableRefreshRateConfigs : RefreshRateConfigs {
     using RefreshRateConfigs::RefreshRateConfigs;
+    using RefreshRateConfigs::RefreshRateOrder;
+
+    void setActiveModeId(DisplayModeId modeId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return RefreshRateConfigs::setActiveModeId(modeId);
+    }
+
+    const DisplayMode& getActiveMode() const {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return RefreshRateConfigs::getActiveMode();
+    }
 
     DisplayModePtr getMinSupportedRefreshRate() const {
         std::lock_guard lock(mLock);
@@ -56,19 +69,41 @@
         return getMinRefreshRateByPolicyLocked();
     }
 
+    DisplayModePtr getMaxRefreshRateByPolicy() const {
+        std::lock_guard lock(mLock);
+        return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
+    }
+
+    std::vector<RefreshRateRanking> getRefreshRatesByPolicy(
+            std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
+        std::lock_guard lock(mLock);
+        return RefreshRateConfigs::
+                getRefreshRatesByPolicyLocked(anchorGroupOpt, refreshRateOrder,
+                                              /*preferredDisplayModeOpt*/ std::nullopt);
+    }
+
     const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
 
-    using RefreshRateConfigs::GetBestRefreshRateCache;
-    auto& mutableGetBestRefreshRateCache() { return mGetBestRefreshRateCache; }
+    using RefreshRateConfigs::GetRankedRefreshRatesCache;
+    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; }
 
-    auto getBestRefreshRateAndSignals(const std::vector<LayerRequirement>& layers,
-                                      GlobalSignals signals) const {
-        return RefreshRateConfigs::getBestRefreshRate(layers, signals);
+    auto getRankedRefreshRatesAndSignals(const std::vector<LayerRequirement>& layers,
+                                         GlobalSignals signals) const {
+        return RefreshRateConfigs::getRankedRefreshRates(layers, signals);
     }
 
     DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
                                       GlobalSignals signals = {}) const {
-        return getBestRefreshRateAndSignals(layers, signals).first;
+        return getRankedRefreshRatesAndSignals(layers, signals).first.front().displayModePtr;
+    }
+
+    SetPolicyResult setPolicy(const PolicyVariant& policy) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return RefreshRateConfigs::setPolicy(policy);
+    }
+
+    SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) {
+        return setPolicy(policy);
     }
 };
 
@@ -155,9 +190,33 @@
 }
 
 TEST_F(RefreshRateConfigsTest, invalidPolicy) {
-    RefreshRateConfigs configs(kModes_60, kModeId60);
-    EXPECT_LT(configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0);
-    EXPECT_LT(configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}), 0);
+    TestableRefreshRateConfigs configs(kModes_60, kModeId60);
+
+    EXPECT_EQ(SetPolicyResult::Invalid,
+              configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
+    EXPECT_EQ(SetPolicyResult::Invalid,
+              configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
+}
+
+TEST_F(RefreshRateConfigsTest, unchangedPolicy) {
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+
+    // Override to the same policy.
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              configs.setPolicy(RefreshRateConfigs::OverridePolicy{kModeId90, {60_Hz, 90_Hz}}));
+
+    // Clear override to restore DisplayManagerPolicy.
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              configs.setPolicy(RefreshRateConfigs::NoOverridePolicy{}));
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
 }
 
 TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) {
@@ -188,7 +247,8 @@
     EXPECT_EQ(kMode60, minRate60);
     EXPECT_EQ(kMode60, performanceRate60);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
     configs.setActiveModeId(kModeId90);
 
     const auto minRate90 = configs.getMinRefreshRateByPolicy();
@@ -211,7 +271,8 @@
     EXPECT_EQ(kMode60, minRate60);
     EXPECT_EQ(kMode60, performanceRate60);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
     configs.setActiveModeId(kModeId90);
 
     const auto minRate90 = configs.getMinRefreshRateByPolicy();
@@ -231,7 +292,8 @@
     EXPECT_EQ(kMode60, minRate);
     EXPECT_EQ(kMode90, performanceRate);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
 
     const auto minRate60 = configs.getMinRefreshRateByPolicy();
     const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
@@ -243,20 +305,21 @@
 TEST_F(RefreshRateConfigsTest, twoModes_getActiveMode) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
     {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId60);
+        const auto& mode = configs.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId60);
     }
 
     configs.setActiveModeId(kModeId90);
     {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId90);
+        const auto& mode = configs.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId90);
     }
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
     {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId90);
+        const auto& mode = configs.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId90);
     }
 }
 
@@ -268,19 +331,33 @@
         // range.
         EXPECT_EQ(kMode90, configs.getBestRefreshRate());
 
-        EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), NO_ERROR);
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
         EXPECT_EQ(kMode60, configs.getBestRefreshRate());
     }
     {
         // We select max even when this will cause a non-seamless switch.
         TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
         constexpr bool kAllowGroupSwitching = true;
-        EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}),
-                  NO_ERROR);
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  configs.setDisplayManagerPolicy(
+                          {kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}));
         EXPECT_EQ(kMode90_G1, configs.getBestRefreshRate());
     }
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) {
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId72);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].vote = LayerVoteType::ExplicitExact;
+    layers[0].desiredRefreshRate = 120_Hz;
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+}
+
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
@@ -317,7 +394,8 @@
     EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.name = "";
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
@@ -341,7 +419,8 @@
     lr.desiredRefreshRate = 24_Hz;
     EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
@@ -365,7 +444,8 @@
     lr.desiredRefreshRate = 24_Hz;
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
@@ -564,9 +644,10 @@
     TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
                                        {.frameRateMultipleThreshold = 120});
 
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
     auto& lr2 = layers[1];
+    auto& lr3 = layers[2];
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -639,6 +720,48 @@
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "120Hz ExplicitDefault";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::ExplicitExact;
+    lr2.name = "120Hz ExplicitExact";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 10_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "120Hz ExplicitExact";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+
+    lr1.desiredRefreshRate = 30_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.name = "30Hz ExplicitExactOrMultiple";
+    lr3.vote = LayerVoteType::Heuristic;
+    lr3.desiredRefreshRate = 120_Hz;
+    lr3.name = "120Hz Heuristic";
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
@@ -923,13 +1046,154 @@
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
+TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicy) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
+    const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode90},
+                                                                   RefreshRateRanking{kMode60},
+                                                                   RefreshRateRanking{kMode30}};
+
+    const std::vector<RefreshRateRanking>& refreshRates =
+            configs.getRefreshRatesByPolicy(configs.getActiveMode().getGroup(),
+                                            TestableRefreshRateConfigs::RefreshRateOrder::
+                                                    Descending);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicy) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
+    const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode30},
+                                                                   RefreshRateRanking{kMode60},
+                                                                   RefreshRateRanking{kMode90}};
+
+    const std::vector<RefreshRateRanking>& refreshRates =
+            configs.getRefreshRatesByPolicy(configs.getActiveMode().getGroup(),
+                                            TestableRefreshRateConfigs::RefreshRateOrder::
+                                                    Ascending);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
+    const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode30},
+                                                                   RefreshRateRanking{kMode60},
+                                                                   RefreshRateRanking{kMode90}};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
+
+    const std::vector<RefreshRateRanking>& refreshRates =
+            configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
+                                            TestableRefreshRateConfigs::RefreshRateOrder::
+                                                    Ascending);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
+    const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode90},
+                                                                   RefreshRateRanking{kMode60},
+                                                                   RefreshRateRanking{kMode30}};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
+
+    const std::vector<RefreshRateRanking>& refreshRates =
+            configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
+                                            TestableRefreshRateConfigs::RefreshRateOrder::
+                                                    Descending);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) {
+    RefreshRateConfigs configs(kModes_60_90, kModeId60);
+    std::vector<RefreshRateRanking> expectedRefreshRates = {RefreshRateRanking{kMode90},
+                                                            RefreshRateRanking{kMode60}};
+
+    auto [refreshRates, signals] = configs.getRankedRefreshRates({}, {});
+    EXPECT_FALSE(signals.powerOnImminent);
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+
+    std::tie(refreshRates, signals) = configs.getRankedRefreshRates({}, {.powerOnImminent = true});
+    EXPECT_TRUE(signals.powerOnImminent);
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr1 = layers[0];
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+
+    std::tie(refreshRates, signals) =
+            configs.getRankedRefreshRates(layers, {.powerOnImminent = true});
+    EXPECT_TRUE(signals.powerOnImminent);
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+
+    expectedRefreshRates = {RefreshRateRanking{kMode60}, RefreshRateRanking{kMode90}};
+    std::tie(refreshRates, signals) =
+            configs.getRankedRefreshRates(layers, {.powerOnImminent = false});
+    EXPECT_FALSE(signals.powerOnImminent);
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
 TEST_F(RefreshRateConfigsTest, touchConsidered) {
     RefreshRateConfigs configs(kModes_60_90, kModeId60);
 
-    auto [_, signals] = configs.getBestRefreshRate({}, {});
+    auto [_, signals] = configs.getRankedRefreshRates({}, {});
     EXPECT_FALSE(signals.touch);
 
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate({}, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates({}, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
@@ -942,16 +1206,16 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr1.name = "60Hz ExplicitDefault";
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
     EXPECT_FALSE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -960,16 +1224,16 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr1.name = "60Hz ExplicitDefault";
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
     EXPECT_FALSE(signals.touch);
 }
 
@@ -1068,36 +1332,14 @@
             EXPECT_EQ(lr.desiredRefreshRate, configs.getBestRefreshRate(layers)->getFps());
         }
     }
-
-    // Test that 23.976 will choose 24 if 23.976 is not supported
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.vote = LayerVoteType::ExplicitExact;
-        lr.desiredRefreshRate = 23.976_Hz;
-        lr.name = "ExplicitExact 23.976 Hz";
-        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId());
-    }
-
-    // Test that 24 will choose 23.976 if 24 is not supported
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.desiredRefreshRate = 24_Hz;
-        lr.name = "ExplicitExact 24 Hz";
-        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
-    }
 }
 
 TEST_F(RefreshRateConfigsTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
-    RefreshRateConfigs configs(kModes_60_90, kModeId90);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1107,9 +1349,10 @@
     lr.name = "60Hz ExplicitDefault";
     lr.focused = true;
 
-    const auto [mode, signals] = configs.getBestRefreshRate(layers, {.touch = true, .idle = true});
+    const auto [mode, signals] =
+            configs.getRankedRefreshRates(layers, {.touch = true, .idle = true});
 
-    EXPECT_EQ(mode, kMode60);
+    EXPECT_EQ(mode.begin()->displayModePtr, kMode60);
     EXPECT_FALSE(signals.touch);
 }
 
@@ -1117,7 +1360,8 @@
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1129,14 +1373,148 @@
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.idle = true}));
 }
 
+TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) {
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+    auto& lr3 = layers[2];
+    auto& lr4 = layers[3];
+    auto& lr5 = layers[4];
+
+    lr1.desiredRefreshRate = 90_Hz;
+    lr1.name = "90Hz";
+    lr1.focused = true;
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+    lr2.focused = true;
+
+    lr3.desiredRefreshRate = 72_Hz;
+    lr3.name = "72Hz";
+    lr3.focused = true;
+
+    lr4.desiredRefreshRate = 120_Hz;
+    lr4.name = "120Hz";
+    lr4.focused = true;
+
+    lr5.desiredRefreshRate = 30_Hz;
+    lr5.name = "30Hz";
+    lr5.focused = true;
+
+    std::vector<RefreshRateRanking> expectedRankings = {
+            RefreshRateRanking{kMode120}, RefreshRateRanking{kMode90}, RefreshRateRanking{kMode72},
+            RefreshRateRanking{kMode60},  RefreshRateRanking{kMode30},
+    };
+
+    std::vector<RefreshRateRanking> actualOrder =
+            configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+    ASSERT_EQ(expectedRankings.size(), actualOrder.size());
+    for (size_t i = 0; i < expectedRankings.size(); ++i) {
+        EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
+                << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+    }
+
+    lr1.vote = LayerVoteType::Max;
+    lr1.name = "Max";
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+
+    lr3.desiredRefreshRate = 72_Hz;
+    lr3.name = "72Hz";
+
+    lr4.desiredRefreshRate = 90_Hz;
+    lr4.name = "90Hz";
+
+    lr5.desiredRefreshRate = 120_Hz;
+    lr5.name = "120Hz";
+
+    expectedRankings = {
+            RefreshRateRanking{kMode120}, RefreshRateRanking{kMode90}, RefreshRateRanking{kMode72},
+            RefreshRateRanking{kMode60},  RefreshRateRanking{kMode30},
+    };
+
+    actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+
+    ASSERT_EQ(expectedRankings.size(), actualOrder.size());
+    for (size_t i = 0; i < expectedRankings.size(); ++i) {
+        EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
+                << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+    }
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 30_Hz;
+    lr1.name = "30Hz";
+
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.name = "120Hz";
+
+    lr3.desiredRefreshRate = 60_Hz;
+    lr3.name = "60Hz";
+
+    lr5.desiredRefreshRate = 72_Hz;
+    lr5.name = "72Hz";
+
+    expectedRankings = {
+            RefreshRateRanking{kMode30},  RefreshRateRanking{kMode60}, RefreshRateRanking{kMode90},
+            RefreshRateRanking{kMode120}, RefreshRateRanking{kMode72},
+    };
+
+    actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+    ASSERT_EQ(expectedRankings.size(), actualOrder.size());
+    for (size_t i = 0; i < expectedRankings.size(); ++i) {
+        EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
+                << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+    }
+
+    lr1.desiredRefreshRate = 120_Hz;
+    lr1.name = "120Hz";
+    lr1.weight = 0.0f;
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+    lr2.vote = LayerVoteType::NoVote;
+
+    lr3.name = "60Hz-2";
+    lr3.vote = LayerVoteType::Heuristic;
+
+    lr4.vote = LayerVoteType::ExplicitExact;
+
+    lr5.desiredRefreshRate = 120_Hz;
+    lr5.name = "120Hz-2";
+
+    expectedRankings = {
+            RefreshRateRanking{kMode90}, RefreshRateRanking{kMode60}, RefreshRateRanking{kMode120},
+            RefreshRateRanking{kMode72}, RefreshRateRanking{kMode30},
+    };
+
+    actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+    ASSERT_EQ(expectedRankings.size(), actualOrder.size());
+    for (size_t i = 0; i < expectedRankings.size(); ++i) {
+        EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
+                << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
 TEST_F(RefreshRateConfigsTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
-    const auto [mode, signals] = configs.getBestRefreshRateAndSignals({}, {});
-    EXPECT_EQ(mode, kMode90);
+    const auto [mode, signals] = configs.getRankedRefreshRatesAndSignals({}, {});
+    EXPECT_EQ(mode.front().displayModePtr, kMode90);
     EXPECT_FALSE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1207,10 +1585,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1225,10 +1603,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     // Verify that we won't change the group if seamless switch is required.
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1244,10 +1622,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1265,10 +1643,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1289,10 +1667,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1318,10 +1696,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1351,10 +1729,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1382,10 +1760,10 @@
     TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
 
     // Allow group switching.
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1405,10 +1783,10 @@
     TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60);
 
     // Allow group switching.
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
                                              .vote = LayerVoteType::ExplicitDefault,
@@ -1437,10 +1815,10 @@
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90);
 
     // Allow group switching.
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {
             {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
@@ -1468,7 +1846,8 @@
         return configs.getBestRefreshRate(layers, {.touch = args.touch})->getId();
     };
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}));
 
     EXPECT_EQ(kModeId60, configs.getBestRefreshRate()->getId());
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
@@ -1492,7 +1871,8 @@
     EXPECT_EQ(kModeId60,
               getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}));
 
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
@@ -1513,14 +1893,16 @@
         layers[0].desiredRefreshRate = 90_Hz;
 
         const auto [refreshRate, signals] =
-                configs.getBestRefreshRateAndSignals(layers, {.touch = touchActive, .idle = true});
+                configs.getRankedRefreshRatesAndSignals(layers,
+                                                        {.touch = touchActive, .idle = true});
 
         // Refresh rate will be chosen by either touch state or idle state.
         EXPECT_EQ(!touchActive, signals.idle);
-        return refreshRate->getId();
+        return refreshRate.front().displayModePtr->getId();
     };
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
     // Idle should be lower priority than touch boost.
     {
@@ -1676,24 +2058,26 @@
     using GlobalSignals = RefreshRateConfigs::GlobalSignals;
     const auto args = std::make_pair(std::vector<LayerRequirement>{},
                                      GlobalSignals{.touch = true, .idle = true});
-    const auto result = std::make_pair(kMode90, GlobalSignals{.touch = true});
 
-    configs.mutableGetBestRefreshRateCache() = {args, result};
+    const auto result = std::make_pair(std::vector<RefreshRateRanking>{RefreshRateRanking{kMode90}},
+                                       GlobalSignals{.touch = true});
 
-    EXPECT_EQ(result, configs.getBestRefreshRateAndSignals(args.first, args.second));
+    configs.mutableGetRankedRefreshRatesCache() = {args, result};
+
+    EXPECT_EQ(result, configs.getRankedRefreshRatesAndSignals(args.first, args.second));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
     TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
 
-    EXPECT_FALSE(configs.mutableGetBestRefreshRateCache());
+    EXPECT_FALSE(configs.mutableGetRankedRefreshRatesCache());
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true};
 
-    const auto result = configs.getBestRefreshRateAndSignals(layers, globalSignals);
+    const auto result = configs.getRankedRefreshRatesAndSignals(layers, globalSignals);
 
-    const auto& cache = configs.mutableGetBestRefreshRateCache();
+    const auto& cache = configs.mutableGetRankedRefreshRatesCache();
     ASSERT_TRUE(cache);
 
     EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
@@ -1814,71 +2198,78 @@
 TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
     using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
 
-    RefreshRateConfigs configs(kModes_60_90, kModeId90);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
 
-    // SetPolicy(60, 90), current 90Hz => TurnOn.
+    // setPolicy(60, 90), current 90Hz => TurnOn.
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
-    // SetPolicy(60, 90), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}), 0);
+    // setPolicy(60, 90), current 60Hz => TurnOn.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
-    // SetPolicy(60, 60), current 60Hz => TurnOff
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    // setPolicy(60, 60), current 60Hz => TurnOff
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 
-    // SetPolicy(90, 90), current 90Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
+    // setPolicy(90, 90), current 90Hz => TurnOff.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 }
 
 TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
     using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
 
-    RefreshRateConfigs configs(kModes_60_120, kModeId120);
+    TestableRefreshRateConfigs configs(kModes_60_120, kModeId120);
 
-    // SetPolicy(0, 60), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}), 0);
+    // setPolicy(0, 60), current 60Hz => TurnOn.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
-    // SetPolicy(60, 60), current 60Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    // setPolicy(60, 60), current 60Hz => TurnOff.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 
-    // SetPolicy(60, 120), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}), 0);
+    // setPolicy(60, 120), current 60Hz => TurnOn.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
-    // SetPolicy(120, 120), current 120Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}), 0);
+    // setPolicy(120, 120), current 120Hz => TurnOff.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateDivisor) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
 
     const auto frameRate = 30_Hz;
-    Fps displayRefreshRate = configs.getActiveMode()->getFps();
+    Fps displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId60);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId72);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId90);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId120);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     configs.setActiveModeId(kModeId90);
-    displayRefreshRate = configs.getActiveMode()->getFps();
+    displayRefreshRate = configs.getActiveMode().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
 
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz));
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 6752a39..ac63a0e 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -21,8 +21,6 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -59,8 +57,8 @@
     static constexpr int32_t PRIORITY_UNSET = -1;
 
     void setupScheduler();
-    sp<BufferStateLayer> createBufferStateLayer();
-    sp<EffectLayer> createEffectLayer();
+    sp<Layer> createBufferStateLayer();
+    sp<Layer> createEffectLayer();
 
     void setParent(Layer* child, Layer* parent);
     void commitTransaction(Layer* layer);
@@ -88,18 +86,17 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-
-sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
+sp<Layer> RefreshRateSelectionTest::createBufferStateLayer() {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
                            LayerMetadata());
-    return sp<BufferStateLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
-sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() {
+sp<Layer> RefreshRateSelectionTest::createEffectLayer() {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
-    return sp<EffectLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
diff --git a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
index f19e554..409e1ef 100644
--- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
@@ -106,40 +106,6 @@
                 testing::Eq(0.0));
 }
 
-// workaround for b/133849373
-TEST_F(RegionSamplingTest, orientation_90) {
-    std::generate(buffer.begin(), buffer.end(),
-                  [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; });
-
-    Rect tl_region{0, 0, 4, 4};
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
-                           tl_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
-                           tl_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
-                           tl_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
-                           tl_region),
-                testing::Eq(0.0));
-
-    Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight};
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
-                           br_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
-                           br_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
-                           br_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
-                           br_region),
-                testing::Eq(1.0));
-}
-
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 53e49eb..406d2bc 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -20,6 +20,7 @@
 
 #include <mutex>
 
+#include "FakeDisplayInjector.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/RefreshRateConfigs.h"
 #include "TestableScheduler.h"
@@ -40,6 +41,7 @@
 
 using MockEventThread = android::mock::EventThread;
 using MockLayer = android::mock::MockLayer;
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
 
 constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
 
@@ -59,11 +61,14 @@
 
     SchedulerTest();
 
-    static inline const DisplayModePtr kMode60 = createDisplayMode(DisplayModeId(0), 60_Hz);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(DisplayModeId(1), 120_Hz);
+    static inline const DisplayModePtr kMode60_1 = createDisplayMode(DisplayModeId(0), 60_Hz);
+    static inline const DisplayModePtr kMode120_1 = createDisplayMode(DisplayModeId(1), 120_Hz);
+    static inline const DisplayModePtr kMode60_2 = createDisplayMode(DisplayModeId(2), 60_Hz);
+    static inline const DisplayModePtr kMode120_2 = createDisplayMode(DisplayModeId(3), 120_Hz);
+    static inline const DisplayModePtr kMode60_3 = createDisplayMode(DisplayModeId(4), 60_Hz);
 
     std::shared_ptr<RefreshRateConfigs> mConfigs =
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60), kMode60->getId());
+            std::make_shared<RefreshRateConfigs>(makeModes(kMode60_1), kMode60_1->getId());
 
     mock::SchedulerCallback mSchedulerCallback;
     TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
@@ -71,6 +76,7 @@
     ConnectionHandle mConnectionHandle;
     MockEventThread* mEventThread;
     sp<MockEventThreadConnection> mEventThreadConnection;
+    FakeDisplayInjector mFakeDisplayInjector;
 
     TestableSurfaceFlinger mFlinger;
 };
@@ -166,7 +172,7 @@
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
     mScheduler->chooseRefreshRateForContent();
 }
 
@@ -176,7 +182,8 @@
     ASSERT_EQ(1u, mScheduler->layerHistorySize());
 
     mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+            std::make_shared<RefreshRateConfigs>(makeModes(kMode60_1, kMode120_1),
+                                                 kMode60_1->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -215,12 +222,18 @@
 }
 
 MATCHER(Is120Hz, "") {
-    return isApproxEqual(arg->getFps(), 120_Hz);
+    return isApproxEqual(arg.front().displayModePtr->getFps(), 120_Hz);
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
-    mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+    auto display = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+            },
+            mFlinger);
+
+    mScheduler->registerDisplay(display);
+    mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
 
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
@@ -233,12 +246,137 @@
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(Is120Hz(), _)).Times(1);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1);
     mScheduler->chooseRefreshRateForContent();
 
     // No-op if layer requirements have not changed.
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
     mScheduler->chooseRefreshRateForContent();
 }
 
+TEST_F(SchedulerTest, getBestDisplayMode_singleDisplay) {
+    auto display = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+            },
+            mFlinger);
+    mScheduler->registerDisplay(display);
+
+    std::vector<RefreshRateConfigs::LayerRequirement> layers =
+            std::vector<RefreshRateConfigs::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+    mScheduler->setContentRequirements(layers);
+    GlobalSignals globalSignals = {.idle = true};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+    std::vector<DisplayModeConfig> displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(1ul, displayModeConfigs.size());
+    EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode60_1);
+    EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+    globalSignals = {.idle = false};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(1ul, displayModeConfigs.size());
+    EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode120_1);
+    EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+    globalSignals = {.touch = true};
+    mScheduler->replaceTouchTimer(10);
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(1ul, displayModeConfigs.size());
+    EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode120_1);
+    EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+    mScheduler->unregisterDisplay(display->getPhysicalId());
+    EXPECT_TRUE(mScheduler->mutableDisplays().empty());
+}
+
+TEST_F(SchedulerTest, getBestDisplayModes_multipleDisplays) {
+    auto display1 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+            },
+            mFlinger);
+    auto display2 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_2, kMode120_2), kMode60_2->getId());
+            },
+            mFlinger, /* port */ 253u);
+    mScheduler->registerDisplay(display1);
+    mScheduler->registerDisplay(display2);
+
+    std::vector<sp<DisplayDevice>> expectedDisplays = {display1, display2};
+    std::vector<RefreshRateConfigs::LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    GlobalSignals globalSignals = {.idle = true};
+    std::vector<DisplayModeConfig> expectedConfigs = {DisplayModeConfig{globalSignals, kMode60_1},
+                                                      DisplayModeConfig{globalSignals, kMode60_2}};
+
+    mScheduler->setContentRequirements(layers);
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    std::vector<DisplayModeConfig> displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(displayModeConfigs.size(), expectedConfigs.size());
+    for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+        EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+                << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+                << " Actual fps "
+                << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+        EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+    }
+
+    expectedConfigs = std::vector<DisplayModeConfig>{DisplayModeConfig{globalSignals, kMode120_1},
+                                                     DisplayModeConfig{globalSignals, kMode120_2}};
+
+    globalSignals = {.idle = false};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
+    for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+        EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+                << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+                << " Actual fps "
+                << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+        EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+    }
+
+    globalSignals = {.touch = true};
+    mScheduler->replaceTouchTimer(10);
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
+    for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+        EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+                << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+                << " Actual fps "
+                << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+        EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+    }
+
+    // Filters out the 120Hz as it's not present on the display3, even with touch active
+    // we select 60Hz here.
+    auto display3 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_3), kMode60_3->getId());
+            },
+            mFlinger, /* port */ 252u);
+    mScheduler->registerDisplay(display3);
+
+    expectedDisplays = {display1, display2, display3};
+    globalSignals = {.touch = true};
+    mScheduler->replaceTouchTimer(10);
+    expectedConfigs = std::vector<DisplayModeConfig>{DisplayModeConfig{globalSignals, kMode60_1},
+                                                     DisplayModeConfig{globalSignals, kMode60_2},
+                                                     DisplayModeConfig{globalSignals, kMode60_3}};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
+    for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+        EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+                << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+                << " Actual fps "
+                << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+        EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+    }
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 6ee8174..dfcfd91 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -24,8 +24,6 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
-#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"
@@ -374,11 +372,6 @@
     const auto& layerFactory = GetParam();
 
     auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    if (!parent->isVisible()) {
-        // This is a hack as all the test layers except EffectLayer are not visible,
-        // but since the logic is unified in Layer, it should be fine.
-        return;
-    }
 
     auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
     addChild(parent, child);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index f7d34ac..6a9c970 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -30,9 +30,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
     // --------------------------------------------------------------------
     // Invocation
 
@@ -61,9 +58,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
     // --------------------------------------------------------------------
     // Invocation
     int64_t oldId = IPCThreadState::self()->clearCallingIdentity();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index 40ef949..93a3811 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -37,9 +37,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
     // Destroying the display commits a display transaction.
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index b607b88..6b7e353 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -20,6 +20,7 @@
 
 #include "DisplayTransactionTestHelpers.h"
 
+#include <ftl/fake_guard.h>
 #include <scheduler/Fps.h>
 
 namespace android {
@@ -40,18 +41,17 @@
         PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
         PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
 
+        DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
+        auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
+
+        setupScheduler(configs);
+
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+        mFlinger.configureAndCommit();
 
-        {
-            DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
-            auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
-
-            mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
-                               .setDisplayModes(std::move(modes), kModeId60, std::move(configs))
-                               .inject();
-        }
-
-        setupScheduler(mDisplay->holdRefreshRateConfigs());
+        mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+                           .setDisplayModes(std::move(modes), kModeId60, std::move(configs))
+                           .inject();
 
         // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
         // will call setActiveConfig instead of setActiveConfigWithConstraints.
@@ -112,8 +112,10 @@
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -122,7 +124,7 @@
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -135,7 +137,7 @@
 
     Mock::VerifyAndClearExpectations(mComposer);
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     // Verify that the next commit will complete the mode change and send
     // a onModeChanged event to the framework.
@@ -145,10 +147,12 @@
     Mock::VerifyAndClearExpectations(mAppEventThread);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
 
     mFlinger.onActiveDisplayChanged(mDisplay);
@@ -158,7 +162,7 @@
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -173,15 +177,17 @@
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     // 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(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -215,12 +221,14 @@
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId120);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -229,7 +237,7 @@
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -264,7 +272,7 @@
     mDisplay = mFlinger.getDisplay(displayToken);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90_4K);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index bc9e801..94d517a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -90,15 +90,12 @@
     Case::HdrSupport::setupComposerCallExpectations(this);
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
 
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
     expectHotplugReceived<Case, true>(mEventThread);
     expectHotplugReceived<Case, true>(mSFEventThread);
 }
 
 template <typename Case>
 void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() {
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
     expectHotplugReceived<Case, false>(mEventThread);
     expectHotplugReceived<Case, false>(mSFEventThread);
 }
@@ -118,9 +115,7 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        expectedPhysical = {.id = *displayId,
-                            .type = *connectionType,
-                            .hwcDisplayId = *hwcDisplayId};
+        expectedPhysical = {.id = *displayId, .hwcDisplayId = *hwcDisplayId};
     }
 
     // The display should have been set up in the current display state
@@ -145,10 +140,13 @@
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
 
-    const auto displayTokenOpt = mFlinger.mutablePhysicalDisplayTokens().get(displayId);
-    ASSERT_TRUE(displayTokenOpt);
+    const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+    ASSERT_TRUE(displayOpt);
 
-    verifyDisplayIsConnected<Case>(displayTokenOpt->get());
+    const auto& display = displayOpt->get();
+    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, display.snapshot().connectionType());
+
+    verifyDisplayIsConnected<Case>(display.token());
 }
 
 void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
@@ -175,7 +173,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    mFlinger.configureAndCommit();
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -204,7 +202,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    mFlinger.configureAndCommit();
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -239,7 +237,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    mFlinger.configureAndCommit();
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -247,10 +245,10 @@
     // HWComposer should not have an entry for the display
     EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // SF should not have a display token.
+    // SF should not have a PhysicalDisplay.
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_FALSE(mFlinger.mutablePhysicalDisplayTokens().contains(displayId));
+    ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
 
     // The existing token should have been removed.
     verifyDisplayIsNotConnected(existing.token());
@@ -321,7 +319,7 @@
                 // --------------------------------------------------------------------
                 // Invocation
 
-                mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+                mFlinger.configureAndCommit();
 
                 // --------------------------------------------------------------------
                 // Postconditions
@@ -329,10 +327,10 @@
                 // HWComposer should not have an entry for the display
                 EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-                // SF should not have a display token.
+                // SF should not have a PhysicalDisplay.
                 const auto displayId = Case::Display::DISPLAY_ID::get();
                 ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-                ASSERT_FALSE(mFlinger.mutablePhysicalDisplayTokens().contains(displayId));
+                ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
             }(),
             testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
@@ -366,7 +364,7 @@
                 // --------------------------------------------------------------------
                 // Invocation
 
-                mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+                mFlinger.configureAndCommit();
 
                 // --------------------------------------------------------------------
                 // Postconditions
@@ -376,9 +374,9 @@
                 const auto displayId = Case::Display::DISPLAY_ID::get();
                 ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
 
-                const auto displayTokenOpt = mFlinger.mutablePhysicalDisplayTokens().get(displayId);
-                ASSERT_TRUE(displayTokenOpt);
-                EXPECT_NE(existing.token(), displayTokenOpt->get());
+                const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+                ASSERT_TRUE(displayOpt);
+                EXPECT_NE(existing.token(), displayOpt->get().token());
 
                 // A new display should be connected in its place.
                 verifyPhysicalDisplayIsConnected<Case>();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index c9a2b00..1210d0b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -20,41 +20,18 @@
 #include "DisplayTransactionTestHelpers.h"
 
 namespace android {
-namespace {
 
 class HotplugTest : public DisplayTransactionTest {};
 
-TEST_F(HotplugTest, enqueuesEventsForDisplayTransaction) {
+TEST_F(HotplugTest, schedulesConfigureToProcessHotplugEvents) {
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(2);
+
     constexpr HWDisplayId hwcDisplayId1 = 456;
-    constexpr HWDisplayId hwcDisplayId2 = 654;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Set the main thread id so that the current thread does not appear to be
-    // the main thread.
-    mFlinger.mutableMainThreadId() = std::thread::id();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // We expect a scheduled commit for the display transaction.
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    // Simulate two hotplug events (a connect and a disconnect)
     mFlinger.onComposerHalHotplug(hwcDisplayId1, Connection::CONNECTED);
+
+    constexpr HWDisplayId hwcDisplayId2 = 654;
     mFlinger.onComposerHalHotplug(hwcDisplayId2, Connection::DISCONNECTED);
 
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display transaction needed flag should be set.
-    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // All events should be in the pending event queue.
     const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
     ASSERT_EQ(2u, pendingEvents.size());
     EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
@@ -63,49 +40,83 @@
     EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
 }
 
-TEST_F(HotplugTest, processesEnqueuedEventsIfCalledOnMainThread) {
-    constexpr HWDisplayId displayId1 = 456;
-
-    // --------------------------------------------------------------------
-    // Note:
-    // --------------------------------------------------------------------
-    // This test case is a bit tricky. We want to verify that
-    // onComposerHalHotplug() calls processDisplayHotplugEventsLocked(), but we
-    // don't really want to provide coverage for everything the later function
-    // does as there are specific tests for it.
-    // --------------------------------------------------------------------
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Set the main thread id so that the current thread does appear to be the
-    // main thread.
-    mFlinger.mutableMainThreadId() = std::this_thread::get_id();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // We expect a scheduled commit for the display transaction.
+TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) {
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(1);
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
-    // --------------------------------------------------------------------
-    // Invocation
-
-    // Simulate a disconnect on a display id that is not connected. This should
-    // be enqueued by onComposerHalHotplug(), and dequeued by
-    // processDisplayHotplugEventsLocked(), but then ignored as invalid.
+    constexpr HWDisplayId displayId1 = 456;
     mFlinger.onComposerHalHotplug(displayId1, Connection::DISCONNECTED);
+    mFlinger.configure();
 
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display transaction needed flag should be set.
-    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // There should be no event queued on return, as it should have been
-    // processed.
+    // The configure stage should consume the hotplug queue and produce a display transaction.
     EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
 }
 
-} // namespace
+TEST_F(HotplugTest, ignoresDuplicateDisconnection) {
+    // Inject a primary display.
+    PrimaryDisplayVariant::injectHwcDisplay(this);
+
+    using ExternalDisplay = ExternalDisplayVariant;
+    ExternalDisplay::setupHwcHotplugCallExpectations(this);
+    ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
+
+    // TODO(b/241286146): Remove this unnecessary call.
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+
+    // A single commit should be scheduled for both configure calls.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+    mFlinger.configure();
+
+    EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+
+    // Disconnecting a display that was already disconnected should be a no-op.
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    mFlinger.configure();
+
+    // The display should be scheduled for removal during the next commit. At this point, it should
+    // still exist but be marked as disconnected.
+    EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+    EXPECT_FALSE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
+}
+
+TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
+    // Inject a primary display.
+    PrimaryDisplayVariant::injectHwcDisplay(this);
+
+    using ExternalDisplay = ExternalDisplayVariant;
+    constexpr bool kFailedHotplug = true;
+    ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this);
+
+    // Simulate a connect event that fails to load display modes due to HWC already having
+    // disconnected the display but SF yet having to process the queued disconnect event.
+    EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _))
+            .WillRepeatedly(Return(Error::BAD_DISPLAY));
+
+    // TODO(b/241286146): Remove this unnecessary call.
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+    mFlinger.configure();
+
+    // The hotplug should be rejected, so no HWComposer::DisplayData should be created.
+    EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+
+    // Disconnecting a display that does not exist should be a no-op.
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    mFlinger.configure();
+
+    EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index ec7e8a7..4e9f293 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -21,6 +21,7 @@
 #include <thread>
 
 #include "DisplayTransactionTestHelpers.h"
+#include "FakeDisplayInjector.h"
 
 #include <android/hardware/power/Boost.h>
 
@@ -32,6 +33,8 @@
 TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
     using namespace std::chrono_literals;
 
+    injectDefaultInternalDisplay([](FakeDisplayDeviceInjector&) {});
+
     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
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index 98249bf..f553a23 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -38,11 +38,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // We expect the surface interceptor to possibly be used, but we treat it as
-    // disabled since it is called as a side effect rather than directly by this
-    // function.
-    EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
-
     // We expect a call to get the active display config.
     Case::Display::setupHwcGetActiveConfigCallExpectations(this);
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index e256d2c..bc66961 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -97,7 +97,7 @@
                     .setNativeWindow(mNativeWindow)
                     .setPowerMode(hal::PowerMode::ON)
                     .inject();
-    mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken();
+    mFlinger.mutableActiveDisplayId() = mDisplay->getPhysicalId();
 }
 
 void SurfaceFlingerPowerHintTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 583cf5f..25857ec 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -140,7 +140,6 @@
 
     static void verifyPostconditions(DisplayTransactionTest* test) {
         EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
-        EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
     }
 };
 
@@ -155,7 +154,6 @@
 
     static void verifyPostconditions(DisplayTransactionTest* test) {
         EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
-        EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
     }
 };
 
@@ -255,14 +253,16 @@
     using DispSync = DispSyncVariant;
     using Transition = TransitionVariant;
 
-    static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
+    static sp<DisplayDevice> injectDisplayWithInitialPowerMode(DisplayTransactionTest* test,
+                                                               PowerMode mode) {
         Display::injectHwcDisplayWithNoDefaultCapabilities(test);
-        auto display = Display::makeFakeExistingDisplayInjector(test);
-        display.inject();
-        display.mutableDisplayDevice()->setPowerMode(mode);
-        if (display.mutableDisplayDevice()->isInternal()) {
-            test->mFlinger.mutableActiveDisplayToken() =
-                    display.mutableDisplayDevice()->getDisplayToken();
+        auto injector = Display::makeFakeExistingDisplayInjector(test);
+        const auto display = injector.inject();
+        display->setPowerMode(mode);
+        if (injector.physicalDisplay()
+                    .transform(&display::PhysicalDisplay::isInternal)
+                    .value_or(false)) {
+            test->mFlinger.mutableActiveDisplayId() = display->getPhysicalId();
         }
 
         return display;
@@ -276,13 +276,6 @@
         EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
     }
 
-    static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
-                                                        PowerMode mode) {
-        EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
-        EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
-                .Times(1);
-    }
-
     static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
         // Any calls to get the active config will return a default value.
         EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
@@ -349,14 +342,12 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
     Case::Transition::template setupCallExpectations<Case>(this);
 
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
-                                  Case::Transition::TARGET_POWER_MODE);
+    mFlinger.setPowerModeInternal(display, Case::Transition::TARGET_POWER_MODE);
 
     // --------------------------------------------------------------------
     // Postconditions
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 6aeb3fe..0384568 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -17,6 +17,8 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <ftl/fake_guard.h>
+
 #include "DisplayHardware/DisplayMode.h"
 
 #include "DisplayTransactionTestHelpers.h"
@@ -34,16 +36,13 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = true;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableHasWideColorDisplay() = true;
+        test->mFlinger.mutableSupportsWideColor() = true;
         test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
 
-        EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
-                                Return(Error::NONE)));
         EXPECT_CALL(*test->mComposer,
                     getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
                 .WillOnce(DoAll(SetArgPointee<2>(
@@ -246,11 +245,20 @@
                                             .setDpiY(DEFAULT_DPI)
                                             .setGroup(0)
                                             .build();
+
         state.physical = {.id = *displayId,
-                          .type = *connectionType,
                           .hwcDisplayId = *hwcDisplayId,
-                          .supportedModes = makeModes(activeMode),
-                          .activeMode = std::move(activeMode)};
+                          .activeMode = activeMode};
+
+        ui::ColorModes colorModes;
+        if constexpr (Case::WideColorSupport::WIDE_COLOR_SUPPORTED) {
+            colorModes.push_back(ColorMode::DISPLAY_P3);
+        }
+
+        mFlinger.mutablePhysicalDisplays().emplace_or_replace(*displayId, displayToken, *displayId,
+                                                              *connectionType,
+                                                              makeModes(activeMode),
+                                                              std::move(colorModes), std::nullopt);
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -262,9 +270,8 @@
     // --------------------------------------------------------------------
     // Postconditions
 
-    ASSERT_TRUE(device != nullptr);
+    ASSERT_NE(nullptr, device);
     EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
-    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
@@ -280,9 +287,8 @@
               device->receivesInput());
 
     if constexpr (Case::Display::CONNECTION_TYPE::value) {
-        EXPECT_EQ(1, device->getSupportedModes().size());
-        EXPECT_NE(nullptr, device->getActiveMode());
-        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode()->getHwcId());
+        ftl::FakeGuard guard(kMainThreadContext);
+        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().getHwcId());
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
new file mode 100644
index 0000000..0cf3bdf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
@@ -0,0 +1,212 @@
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::Return;
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test {
+public:
+    SurfaceFlingerUpdateLayerMetadataSnapshotTest() { setupScheduler(); }
+
+protected:
+    void setupScheduler() {
+        auto eventThread = std::make_unique<mock::EventThread>();
+        auto sfEventThread = std::make_unique<mock::EventThread>();
+
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
+                                                                 mock::EventThread::kCallingUid,
+                                                                 ResyncCallback())));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
+                                                                 mock::EventThread::kCallingUid,
+                                                                 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(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));
+    }
+
+    sp<Layer> createLayer(const char* name, LayerMetadata layerMetadata) {
+        return sp<Layer>::make(
+                LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, layerMetadata});
+    }
+
+    TestableSurfaceFlinger mFlinger;
+};
+
+class LayerMetadataBuilder {
+public:
+    LayerMetadataBuilder(LayerMetadata layerMetadata = {}) : mLayerMetadata(layerMetadata) {}
+
+    LayerMetadataBuilder& setInt32(uint32_t key, int32_t value) {
+        mLayerMetadata.setInt32(key, value);
+        return *this;
+    }
+
+    LayerMetadata build() { return mLayerMetadata; }
+
+private:
+    LayerMetadata mLayerMetadata;
+};
+
+bool operator==(const LayerMetadata& lhs, const LayerMetadata& rhs) {
+    return lhs.mMap == rhs.mMap;
+}
+
+std::ostream& operator<<(std::ostream& stream, const LayerMetadata& layerMetadata) {
+    stream << "LayerMetadata{";
+    for (auto it = layerMetadata.mMap.cbegin(); it != layerMetadata.mMap.cend(); it++) {
+        if (it != layerMetadata.mMap.cbegin()) {
+            stream << ", ";
+        }
+        stream << layerMetadata.itemToString(it->first, ":");
+    }
+    return stream << "}";
+}
+
+// Test that the snapshot's layer metadata is set.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesSnapshotMetadata) {
+    auto layerMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
+    auto layer = createLayer("layer", layerMetadata);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
+}
+
+// Test that snapshot layer metadata is set by merging the child's metadata on top of its
+// parent's metadata.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, mergesSnapshotMetadata) {
+    auto layerAMetadata = LayerMetadataBuilder()
+                                  .setInt32(METADATA_OWNER_UID, 1)
+                                  .setInt32(METADATA_TASK_ID, 2)
+                                  .build();
+    auto layerA = createLayer("parent", layerAMetadata);
+    auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 3).build();
+    auto layerB = createLayer("child", layerBMetadata);
+    layerA->addChild(layerB);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
+    auto expectedChildMetadata =
+            LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build();
+    ASSERT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
+}
+
+// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of
+// that parent's relative layer metadata.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadata) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
+    auto layerA = createLayer("relative-parent", layerAMetadata);
+    auto layerAHandle = layerA->getHandle();
+    auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 2).build();
+    auto layerB = createLayer("relative-child", layerBMetadata);
+    layerB->setRelativeLayer(layerAHandle, 1);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+    ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+}
+
+// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within
+// two other layers.
+//
+// Layer
+//      A
+//     / \
+//    B   D
+//   /
+//  C
+//
+// Z-order Relatives
+//    B <- D <- C
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadataInterleaved) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
+    auto layerA = createLayer("layer-a", layerAMetadata);
+    auto layerBMetadata = LayerMetadataBuilder()
+                                  .setInt32(METADATA_TASK_ID, 2)
+                                  .setInt32(METADATA_OWNER_PID, 3)
+                                  .build();
+    auto layerB = createLayer("layer-b", layerBMetadata);
+    auto layerBHandle = layerB->getHandle();
+    auto layerC = createLayer("layer-c", {});
+    auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build();
+    auto layerD = createLayer("layer-d", layerDMetadata);
+    auto layerDHandle = layerD->getHandle();
+    layerB->addChild(layerC);
+    layerA->addChild(layerB);
+    layerA->addChild(layerD);
+    layerC->setRelativeLayer(layerDHandle, 1);
+    layerD->setRelativeLayer(layerBHandle, 1);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    auto expectedLayerDRelativeMetadata = LayerMetadataBuilder()
+                                                  // From layer A, parent of relative parent
+                                                  .setInt32(METADATA_OWNER_UID, 1)
+                                                  // From layer B, relative parent
+                                                  .setInt32(METADATA_TASK_ID, 2)
+                                                  .setInt32(METADATA_OWNER_PID, 3)
+                                                  .build();
+    ASSERT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
+    auto expectedLayerCRelativeMetadata =
+            LayerMetadataBuilder()
+                    // From layer A, parent of relative parent
+                    .setInt32(METADATA_OWNER_UID, 1)
+                    // From layer B, relative parent of relative parent
+                    .setInt32(METADATA_OWNER_PID, 3)
+                    // From layer D, relative parent
+                    .setInt32(METADATA_TASK_ID, 4)
+                    .build();
+    ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
+}
+
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest,
+       updatesRelativeMetadataMultipleRelativeChildren) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
+    auto layerA = createLayer("layer-a", layerAMetadata);
+    auto layerAHandle = layerA->getHandle();
+    auto layerB = createLayer("layer-b", {});
+    auto layerC = createLayer("layer-c", {});
+    layerB->setRelativeLayer(layerAHandle, 1);
+    layerC->setRelativeLayer(layerAHandle, 2);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerC);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+    ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+    ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 66cdfd7..68df987 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -50,6 +50,7 @@
         });
     }
 
+    MOCK_METHOD(void, scheduleConfigure, (), (override));
     MOCK_METHOD(void, scheduleFrame, (), (override));
     MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
 
@@ -67,6 +68,8 @@
 
     auto& mutableLayerHistory() { return mLayerHistory; }
 
+    auto& mutableDisplays() { return mDisplays; }
+
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
         return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
     }
@@ -93,6 +96,22 @@
         return mPolicy.touch == Scheduler::TouchState::Active;
     }
 
+    void setTouchStateAndIdleTimerPolicy(GlobalSignals globalSignals) {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.touch = globalSignals.touch ? TouchState::Active : TouchState::Inactive;
+        mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
+    }
+
+    void setContentRequirements(std::vector<RefreshRateConfigs::LayerRequirement> layers) {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.contentRequirements = std::move(layers);
+    }
+
+    std::vector<DisplayModeConfig> getBestDisplayModeConfigs() {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        return Scheduler::getBestDisplayModeConfigs();
+    }
+
     void dispatchCachedReportedMode() {
         std::lock_guard<std::mutex> lock(mPolicyLock);
         return Scheduler::dispatchCachedReportedMode();
@@ -109,6 +128,7 @@
 
 private:
     // ICompositor overrides:
+    void configure() override {}
     bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
     void composite(TimePoint, VsyncId) override {}
     void sample() override {}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 407706d..29ff5ee 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -30,9 +30,7 @@
 #include <ftl/fake_guard.h>
 #include <gui/ScreenCaptureResults.h>
 
-#include "BufferStateLayer.h"
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
 #include "FakeVsyncConfiguration.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
@@ -42,7 +40,6 @@
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerDefaultFactory.h"
-#include "SurfaceInterceptor.h"
 #include "TestableScheduler.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
@@ -83,10 +80,6 @@
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return sp<android::impl::SurfaceInterceptor>::make();
-    }
-
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
         return sp<StartPropertySetThread>::make(timestampPropertyValue);
     }
@@ -121,11 +114,9 @@
         return compositionengine::impl::createCompositionEngine();
     }
 
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs&) override {
-        return nullptr;
-    }
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs&) override { return nullptr; }
 
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
+    sp<Layer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
 
     std::unique_ptr<FrameTracer> createFrameTracer() override {
         return std::make_unique<mock::FrameTracer>();
@@ -162,7 +153,6 @@
         if (!mFlinger) {
             mFlinger = sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
         }
-        mFlinger->mAnimationTransactionTimeout = ms2ns(10);
     }
 
     SurfaceFlinger* flinger() { return mFlinger.get(); }
@@ -223,7 +213,7 @@
             configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
         }
 
-        const auto fps = configs->getActiveMode()->getFps();
+        const auto fps = FTL_FAKE_GUARD(kMainThreadContext, configs->getActiveMode().getFps());
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -289,7 +279,7 @@
                                        const sp<NativeHandle>& sidebandStream) {
         layer->mDrawingState.sidebandStream = sidebandStream;
         layer->mSidebandStream = sidebandStream;
-        layer->editCompositionState()->sidebandStream = sidebandStream;
+        layer->editLayerSnapshot()->sidebandStream = sidebandStream;
     }
 
     void setLayerCompositionType(const sp<Layer>& layer,
@@ -317,8 +307,15 @@
      * Forwarding for functions being tested
      */
 
-    TimePoint commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVSyncTime) {
-        mFlinger->commit(frameTime, vsyncId, expectedVSyncTime);
+    void configure() { mFlinger->configure(); }
+
+    void configureAndCommit() {
+        configure();
+        commitTransactionsLocked(eDisplayTransactionNeeded);
+    }
+
+    TimePoint commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
+        mFlinger->commit(frameTime, vsyncId, expectedVsyncTime);
         return frameTime;
     }
 
@@ -362,9 +359,10 @@
                                                        dispSurface, producer);
     }
 
-    auto commitTransactionsLocked(uint32_t transactionFlags) {
+    void commitTransactionsLocked(uint32_t transactionFlags) {
         Mutex::Autolock lock(mFlinger->mStateLock);
-        return mFlinger->commitTransactionsLocked(transactionFlags);
+        ftl::FakeGuard guard(kMainThreadContext);
+        mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
     void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
@@ -414,9 +412,13 @@
         return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
     }
 
-    auto& getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
-    auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
-    auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
+    auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+    auto& getPendingTransactionQueue() {
+        return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+    }
+    size_t getPendingTransactionCount() {
+        return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
+    }
 
     auto setTransactionState(
             const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
@@ -457,26 +459,26 @@
 
     void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
         Mutex::Autolock lock(mFlinger->mStateLock);
+        ftl::FakeGuard guard(kMainThreadContext);
         mFlinger->onActiveDisplayChangedLocked(activeDisplay);
     }
 
-    auto createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
-                     const sp<IBinder>& parentHandle, int32_t* outLayerId,
-                     const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
-        return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, parentLayer,
-                                     outTransformHint);
+    auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
+                     gui::CreateSurfaceResult& outResult) {
+        return mFlinger->createLayer(args, parentHandle, outResult);
     }
 
     auto mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
-                     sp<IBinder>* outHandle, int32_t* outLayerId) {
-        return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
+                     gui::CreateSurfaceResult& outResult) {
+        return mFlinger->mirrorLayer(args, mirrorFromHandle, outResult);
     }
 
+    void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
 
-    const auto& getHasPoweredOff() const { return mFlinger->mHasPoweredOff; }
     const auto& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; }
     auto& getHwComposer() const {
         return static_cast<impl::HWComposer&>(mFlinger->getHwComposer());
@@ -487,32 +489,30 @@
         return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
     }
 
-    nsecs_t getAnimationTransactionTimeout() const {
-        return mFlinger->mAnimationTransactionTimeout;
-    }
-
     /* ------------------------------------------------------------------------
      * Read-write access to private data to set up preconditions and assert
      * post-conditions.
      */
 
     const auto& displays() const { return mFlinger->mDisplays; }
+    const auto& physicalDisplays() const { return mFlinger->mPhysicalDisplays; }
     const auto& currentState() const { return mFlinger->mCurrentState; }
     const auto& drawingState() const { return mFlinger->mDrawingState; }
     const auto& transactionFlags() const { return mFlinger->mTransactionFlags; }
-    const auto& hwcPhysicalDisplayIdMap() const { return getHwComposer().mPhysicalDisplayIdMap; }
 
-    auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
+    const auto& hwcPhysicalDisplayIdMap() const { return getHwComposer().mPhysicalDisplayIdMap; }
+    const auto& hwcDisplayData() const { return getHwComposer().mDisplayData; }
+
+    auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
 
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
     auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
     auto& mutableDisplays() { return mFlinger->mDisplays; }
+    auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
     auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
-    auto& mutableInterceptor() { return mFlinger->mInterceptor; }
     auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
     auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
-    auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; }
     auto& mutableTexturePool() { return mFlinger->mTexturePool; }
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
@@ -521,7 +521,7 @@
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
-    auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
+    auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
 
     auto fromHandle(const sp<IBinder>& handle) {
         return mFlinger->fromHandle(handle);
@@ -535,7 +535,6 @@
         mutableDisplays().clear();
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
-        mutableInterceptor().clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
         mFlinger->mCompositionEngine->setRenderEngine(
@@ -717,13 +716,20 @@
               : mFlinger(flinger),
                 mCreationArgs(flinger.mFlinger, flinger.mFlinger->getHwComposer(), mDisplayToken,
                               display),
+                mConnectionType(connectionType),
                 mHwcDisplayId(hwcDisplayId) {
-            mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
+            mCreationArgs.initialPowerMode = hal::PowerMode::ON;
         }
 
         sp<IBinder> token() const { return mDisplayToken; }
 
+        auto physicalDisplay() const {
+            return ftl::Optional(mCreationArgs.compositionDisplay->getDisplayId())
+                    .and_then(&PhysicalDisplayId::tryCast)
+                    .and_then(display::getPhysicalDisplay(mFlinger.physicalDisplays()));
+        }
+
         DisplayDeviceState& mutableDrawingDisplayState() {
             return mFlinger.mutableDrawingState().displays.editValueFor(mDisplayToken);
         }
@@ -751,7 +757,7 @@
         // the `configs` parameter in favor of an alternative setRefreshRateConfigs API.
         auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId,
                               std::shared_ptr<scheduler::RefreshRateConfigs> configs = nullptr) {
-            mCreationArgs.supportedModes = std::move(modes);
+            mDisplayModes = std::move(modes);
             mCreationArgs.activeModeId = activeModeId;
             mCreationArgs.refreshRateConfigs = std::move(configs);
             return *this;
@@ -797,7 +803,7 @@
         sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
             const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
 
-            auto& modes = mCreationArgs.supportedModes;
+            auto& modes = mDisplayModes;
             auto& activeModeId = mCreationArgs.activeModeId;
 
             if (displayId && !mCreationArgs.refreshRateConfigs) {
@@ -825,8 +831,16 @@
                 }
             }
 
+            sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
+            mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
+            if (mFlinger.scheduler()) {
+                mFlinger.scheduler()->registerDisplay(display);
+            }
+
             DisplayDeviceState state;
-            if (const auto type = mCreationArgs.connectionType) {
+            state.isSecure = mCreationArgs.isSecure;
+
+            if (mConnectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
                 const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
                 LOG_ALWAYS_FATAL_IF(!physicalId);
@@ -836,29 +850,21 @@
                 LOG_ALWAYS_FATAL_IF(!activeMode);
 
                 state.physical = {.id = *physicalId,
-                                  .type = *type,
                                   .hwcDisplayId = *mHwcDisplayId,
-                                  .deviceProductInfo = {},
-                                  .supportedModes = modes,
                                   .activeMode = activeMode->get()};
-            }
 
-            state.isSecure = mCreationArgs.isSecure;
+                const auto it = mFlinger.mutablePhysicalDisplays()
+                                        .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
+                                                            *mConnectionType, std::move(modes),
+                                                            ui::ColorModes(), std::nullopt)
+                                        .first;
 
-            sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
-            if (!display->isVirtual()) {
-                display->setActiveMode(activeModeId);
+                display->setActiveMode(activeModeId, it->second.snapshot());
             }
-            mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
 
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
             mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
 
-            if (const auto& physical = state.physical) {
-                mFlinger.mutablePhysicalDisplayTokens().emplace_or_replace(physical->id,
-                                                                           mDisplayToken);
-            }
-
             return display;
         }
 
@@ -866,6 +872,8 @@
         TestableSurfaceFlinger& mFlinger;
         sp<BBinder> mDisplayToken = sp<BBinder>::make();
         DisplayDeviceCreationArgs mCreationArgs;
+        DisplayModes mDisplayModes;
+        const std::optional<ui::DisplayConnectionType> mConnectionType;
         const std::optional<hal::HWDisplayId> mHwcDisplayId;
     };
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index efb9e0c..db438b7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
 
@@ -22,12 +21,17 @@
 #include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <gui/LayerState.h>
 #include <gui/SurfaceComposerClient.h>
+#include <gui/fake/BufferData.h>
 #include <log/log.h>
 #include <ui/MockFence.h>
 #include <utils/String8.h>
+#include <vector>
+#include <binder/Binder.h>
 
 #include "TestableSurfaceFlinger.h"
+#include "TransactionHandler.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockVsyncController.h"
 
@@ -37,7 +41,7 @@
 using testing::Return;
 
 using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
+constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5);
 class TransactionApplicationTest : public testing::Test {
 public:
     TransactionApplicationTest() {
@@ -78,6 +82,7 @@
         mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
                                 std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
                                 std::move(eventThread), std::move(sfEventThread));
+        mFlinger.flinger()->addTransactionReadyFilters();
     }
 
     TestableSurfaceFlinger mFlinger;
@@ -112,7 +117,7 @@
     void setupSingle(TransactionInfo& transaction, uint32_t flags, int64_t desiredPresentTime,
                      bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo) {
         mTransactionNumber++;
-        transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
+        transaction.flags |= flags;
         transaction.desiredPresentTime = desiredPresentTime;
         transaction.isAutoTimestamp = isAutoTimestamp;
         transaction.frameTimelineInfo = frameTimelineInfo;
@@ -136,11 +141,7 @@
         // If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
         // SF to commit the transaction. If this is animation, it should not time out waiting.
         nsecs_t returnedTime = systemTime();
-        if (flags & ISurfaceComposer::eSynchronous) {
-            EXPECT_GE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
-        } else {
-            EXPECT_LE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
-        }
+        EXPECT_LE(returnedTime, applicationTime + TRANSACTION_TIMEOUT);
         // Each transaction should have been placed on the transaction queue
         auto& transactionQueue = mFlinger.getTransactionQueue();
         EXPECT_FALSE(transactionQueue.isEmpty());
@@ -165,13 +166,7 @@
                                      transaction.id);
 
         nsecs_t returnedTime = systemTime();
-        if (flags & ISurfaceComposer::eSynchronous) {
-            EXPECT_GE(systemTime(),
-                      applicationSentTime + mFlinger.getAnimationTransactionTimeout());
-        } else {
-            EXPECT_LE(returnedTime,
-                      applicationSentTime + mFlinger.getAnimationTransactionTimeout());
-        }
+        EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT);
         // This transaction should have been placed on the transaction queue
         auto& transactionQueue = mFlinger.getTransactionQueue();
         EXPECT_FALSE(transactionQueue.isEmpty());
@@ -204,7 +199,7 @@
         // This thread should not have been blocked by the above transaction
         // (5s is the timeout period that applyTransactionState waits for SF to
         // commit the transaction)
-        EXPECT_LE(systemTime(), applicationSentTime + mFlinger.getAnimationTransactionTimeout());
+        EXPECT_LE(systemTime(), applicationSentTime + TRANSACTION_TIMEOUT);
         // transaction that would goes to pending transaciton queue.
         mFlinger.flushTransactionQueues();
 
@@ -220,13 +215,7 @@
         // if this is an animation, this thread should be blocked for 5s
         // in setTransactionState waiting for transactionA to flush.  Otherwise,
         // the transaction should be placed on the pending queue
-        if (flags & ISurfaceComposer::eSynchronous) {
-            EXPECT_GE(systemTime(),
-                      applicationSentTime + mFlinger.getAnimationTransactionTimeout());
-        } else {
-            EXPECT_LE(systemTime(),
-                      applicationSentTime + mFlinger.getAnimationTransactionTimeout());
-        }
+        EXPECT_LE(systemTime(), applicationSentTime + TRANSACTION_TIMEOUT);
 
         // transaction that would goes to pending transaciton queue.
         mFlinger.flushTransactionQueues();
@@ -294,26 +283,14 @@
     EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
 }
 
-TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) {
-    NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous);
-}
-
 TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) {
     NotPlacedOnTransactionQueue(/*flags*/ 0);
 }
 
-TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) {
-    PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous);
-}
-
 TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) {
     PlaceOnTransactionQueue(/*flags*/ 0);
 }
 
-TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Synchronous) {
-    BlockedByPriorTransaction(ISurfaceComposer::eSynchronous);
-}
-
 TEST_F(TransactionApplicationTest, FromHandle) {
     sp<IBinder> badHandle;
     auto ret = mFlinger.fromHandle(badHandle);
@@ -329,7 +306,6 @@
             mFlinger.getTransactionQueue().pop();
         }
         mFlinger.getPendingTransactionQueue().clear();
-        mFlinger.getTransactionCommittedSignals().clear();
         mFlinger.commitTransactionsLocked(eTransactionMask);
         mFlinger.mutableCurrentState().layersSortedByZ.clear();
         mFlinger.mutableDrawingState().layersSortedByZ.clear();
@@ -343,12 +319,14 @@
 
     ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) {
         ComposerState state;
-        state.state.bufferData = std::make_shared<BufferData>();
+        state.state.bufferData =
+                std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1,
+                                                   /* height */ 2, /* pixelFormat */ 0,
+                                                   /* outUsage */ 0);
         state.state.bufferData->acquireFence = std::move(fence);
         state.state.layerId = layerId;
         state.state.surface =
-                sp<BufferStateLayer>::make(
-                        LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
+                sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
                         ->getHandle();
         state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
 
@@ -362,7 +340,7 @@
     TransactionInfo createTransactionInfo(const sp<IBinder>& applyToken,
                                           const std::vector<ComposerState>& states) {
         TransactionInfo transaction;
-        const uint32_t kFlags = ISurfaceComposer::eSynchronous;
+        const uint32_t kFlags = 0;
         const nsecs_t kDesiredPresentTime = systemTime();
         const bool kIsAutoTimestamp = true;
         const auto kFrameTimelineInfo = FrameTimelineInfo{};
@@ -377,7 +355,6 @@
     }
 
     void setTransactionStates(const std::vector<TransactionInfo>& transactions,
-                              size_t expectedTransactionsApplied,
                               size_t expectedTransactionsPending) {
         EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
         EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
@@ -392,8 +369,7 @@
         }
         mFlinger.flushTransactionQueues();
         EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
-        EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionQueue().size());
-        EXPECT_EQ(expectedTransactionsApplied, mFlinger.getTransactionCommittedSignals().size());
+        EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionCount());
     }
 };
 
@@ -409,22 +385,19 @@
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleUnSignaledFromTheQueue) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
@@ -434,33 +407,13 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
-    const auto kExpectedTransactionsPending = 1u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eCropChanged),
-                                  });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -472,15 +425,31 @@
                                                                       layer_state_t::
                                                                               eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId = 1;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eCropChanged |
+                                                                      layer_state_t::
+                                                                              eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueueSameApplyTokenMultiState) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto mixedTransaction =
@@ -493,8 +462,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueue_MultipleStateTransaction) {
@@ -502,7 +470,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto mixedTransaction =
@@ -515,8 +482,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSignaledFromTheQueue) {
@@ -524,7 +490,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
@@ -541,8 +506,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest,
@@ -553,7 +517,6 @@
     const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -580,43 +543,7 @@
                                   });
 
     setTransactionStates({unsignaledTransaction, signaledTransaction, signaledTransaction2},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAutoSingleLayerTest, UnsignaledNotAppliedWhenThereAreSignaled_SignaledFirst) {
-    const sp<IBinder> kApplyToken1 =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
-    const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
-    const auto kExpectedTransactionsPending = 1u;
-
-    const auto signaledTransaction =
-            createTransactionInfo(kApplyToken1,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto signaledTransaction2 =
-            createTransactionInfo(kApplyToken2,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken3,
-                                  {
-                                          createComposerState(kLayerId2,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-
-    setTransactionStates({signaledTransaction, signaledTransaction2, unsignaledTransaction},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
+                         kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
@@ -624,7 +551,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -641,7 +567,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({unsignaledTransaction, signaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -651,7 +577,6 @@
     const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -669,14 +594,13 @@
                                                               layer_state_t::eBufferChanged),
                                   });
     setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
+                         kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, DontLatchUnsignaledWhenEarlyOffset) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -690,8 +614,7 @@
     // Get VsyncModulator out of the default config
     static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
 
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 class LatchUnsignaledDisabledTest : public LatchUnsignaledTest {
@@ -706,22 +629,19 @@
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueue) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -731,15 +651,13 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueSameLayerId) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -752,8 +670,7 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueDifferentLayerId) {
@@ -761,7 +678,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -774,8 +690,7 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
@@ -783,7 +698,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
@@ -800,8 +714,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueDifferentApplyToken) {
@@ -810,7 +723,6 @@
     const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -827,7 +739,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({unsignaledTransaction, signaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -836,7 +748,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto signaledTransaction =
@@ -853,7 +764,7 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({signaledTransaction, unsignaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -862,8 +773,7 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 0u;
-    const auto kExpectedTransactionsPending = 1u;
+    const auto kExpectedTransactionsPending = 2u;
 
     const auto unsignaledTransaction =
             createTransactionInfo(kApplyToken,
@@ -880,7 +790,7 @@
                                                               layer_state_t::eBufferChanged),
                                   });
     setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
+                         kExpectedTransactionsPending);
 }
 
 class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest {
@@ -895,37 +805,32 @@
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueue) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto mixedTransaction =
@@ -934,8 +839,7 @@
                                                        layer_state_t::eBufferChanged),
                                    createComposerState(kLayerId, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) {
@@ -943,7 +847,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto mixedTransaction =
@@ -952,8 +855,7 @@
                                                        layer_state_t::eBufferChanged),
                                    createComposerState(kLayerId2, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
@@ -961,7 +863,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
@@ -978,8 +879,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) {
@@ -988,7 +888,6 @@
     const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
@@ -1005,7 +904,7 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({signaledTransaction, unsignaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -1014,7 +913,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
@@ -1031,7 +929,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({unsignaledTransaction, signaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -1041,7 +939,6 @@
     const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
@@ -1059,14 +956,13 @@
                                                               layer_state_t::eBufferChanged),
                                   });
     setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
+                         kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
@@ -1080,8 +976,19 @@
     // Get VsyncModulator out of the default config
     static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
 
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
+}
+
+TEST(TransactionHandlerTest, QueueTransaction) {
+    TransactionHandler handler;
+    TransactionState transaction;
+    transaction.applyToken = sp<BBinder>::make();
+    transaction.id = 42;
+    handler.queueTransaction(std::move(transaction));
+    std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
+
+    EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
+    EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index cd64325..1173d1c 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -56,11 +56,11 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
         LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
                                LayerMetadata());
-        return sp<BufferStateLayer>::make(args);
+        return sp<Layer>::make(args);
     }
 
     void commitTransaction(Layer* layer) {
@@ -101,7 +101,7 @@
     FenceToFenceTimeMap fenceFactory;
 
     void BLASTTransactionSendsFrameTracerEvents() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
         sp<Fence> fence(sp<Fence>::make());
         int32_t layerId = layer->getSequence();
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index 1f011be..14e1aac 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -49,7 +49,8 @@
         ComposerState s;
         if (i == 1) {
             layer.parentSurfaceControlForChild =
-                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), layerHandle, 42);
+                    sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), layerHandle, 42,
+                                             "#42");
         }
         s.state = layer;
         t1.states.add(s);
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index d5823c3..ae03db4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -56,11 +56,10 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
-                               LayerMetadata());
-        return sp<BufferStateLayer>::make(args);
+        LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata());
+        return sp<Layer>::make(args);
     }
 
     void commitTransaction(Layer* layer) {
@@ -101,7 +100,7 @@
     FenceToFenceTimeMap fenceFactory;
 
     void PresentedSurfaceFrameForBufferlessTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         FrameTimelineInfo ftInfo;
         ftInfo.vsyncId = 1;
         ftInfo.inputEventId = 0;
@@ -116,7 +115,7 @@
     }
 
     void PresentedSurfaceFrameForBufferTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         sp<Fence> fence(sp<Fence>::make());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         BufferData bufferData;
@@ -150,7 +149,7 @@
     }
 
     void DroppedSurfaceFrameForBufferTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
         sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
@@ -208,7 +207,7 @@
     }
 
     void BufferlessSurfaceFramePromotedToBufferSurfaceFrame() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         FrameTimelineInfo ftInfo;
         ftInfo.vsyncId = 1;
         ftInfo.inputEventId = 0;
@@ -249,7 +248,7 @@
     }
 
     void BufferlessSurfaceFrameNotCreatedIfBufferSufaceFrameExists() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         sp<Fence> fence(sp<Fence>::make());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         BufferData bufferData;
@@ -275,7 +274,7 @@
     }
 
     void MultipleSurfaceFramesPresentedTogether() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         FrameTimelineInfo ftInfo;
         ftInfo.vsyncId = 1;
         ftInfo.inputEventId = 0;
@@ -336,7 +335,7 @@
     }
 
     void PendingSurfaceFramesRemovedAfterClassification() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
         sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
@@ -388,7 +387,7 @@
     }
 
     void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
         sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
@@ -477,7 +476,7 @@
     }
 
     void MultipleCommitsBeforeLatch() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         uint32_t surfaceFramesPendingClassification = 0;
         std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
         for (int i = 0; i < 10; i += 2) {
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index 45ebb85..da87f1d 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -22,7 +22,6 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferStateLayer.h"
 #include "TestableSurfaceFlinger.h"
 #include "TunnelModeEnabledReporter.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -64,7 +63,7 @@
 
     void setupScheduler();
     void setupComposer(uint32_t virtualDisplayCount);
-    sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
+    sp<Layer> createBufferStateLayer(LayerMetadata metadata);
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
@@ -95,11 +94,10 @@
     mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
 }
 
-sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer(
-        LayerMetadata metadata = {}) {
+sp<Layer> TunnelModeEnabledReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
-    return sp<BufferStateLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 void TunnelModeEnabledReporterTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
new file mode 100644
index 0000000..5049b1d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockAidlPowerHalWrapper.h"
+#include "MockIPower.h"
+
+namespace android::Hwc2::mock {
+
+MockAidlPowerHalWrapper::MockAidlPowerHalWrapper()
+      : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){};
+MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default;
+
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
new file mode 100644
index 0000000..c2c3d77
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+#include <scheduler/Time.h>
+
+#include "DisplayHardware/PowerAdvisor.h"
+
+namespace android {
+namespace hardware {
+namespace power {
+class IPower;
+}
+} // namespace hardware
+} // namespace android
+
+namespace android::Hwc2::mock {
+
+class MockAidlPowerHalWrapper : public Hwc2::impl::AidlPowerHalWrapper {
+public:
+    MockAidlPowerHalWrapper();
+    ~MockAidlPowerHalWrapper() override;
+    MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override));
+    MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
+    MOCK_METHOD(void, restartPowerHintSession, (), (override));
+    MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
+                (override));
+    MOCK_METHOD(bool, startPowerHintSession, (), (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (Duration targetDuration), (override));
+    MOCK_METHOD(void, sendActualWorkDuration, (Duration actualDuration, TimePoint timestamp),
+                (override));
+    MOCK_METHOD(bool, shouldReconnectHAL, (), (override));
+};
+
+} // namespace android::Hwc2::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index aede250..fb1b394 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -36,7 +36,7 @@
     MOCK_METHOD(bool, usePowerHintSession, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
-    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
+    MOCK_METHOD(void, setTargetWorkDuration, (Duration targetDuration), (override));
     MOCK_METHOD(void, sendActualWorkDuration, (), (override));
     MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
     MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
@@ -44,25 +44,24 @@
     MOCK_METHOD(void, setGpuFenceTime,
                 (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
     MOCK_METHOD(void, setHwcValidateTiming,
-                (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+                (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime),
                 (override));
     MOCK_METHOD(void, setHwcPresentTiming,
-                (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+                (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
                 (override));
     MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
     MOCK_METHOD(void, setRequiresClientComposition,
                 (DisplayId displayId, bool requiresClientComposition), (override));
-    MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
-    MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+    MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
+    MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
                 (override));
     MOCK_METHOD(void, setHwcPresentDelayedTime,
-                (DisplayId displayId,
-                 std::chrono::steady_clock::time_point earliestFrameStartTime));
-    MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
-    MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
-    MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+                (DisplayId displayId, TimePoint earliestFrameStartTime));
+    MOCK_METHOD(void, setFrameDelay, (Duration frameDelayDuration), (override));
+    MOCK_METHOD(void, setCommitStart, (TimePoint commitStartTime), (override));
+    MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override));
     MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
-    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override));
+    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index d086d79..0d94f4c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -18,8 +18,6 @@
 
 #include <gmock/gmock.h>
 
-#include "Layer.h"
-
 namespace android::mock {
 
 class MockLayer : public Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 5267586..8af2dfa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -24,14 +24,14 @@
 
 struct SchedulerCallback final : ISchedulerCallback {
     MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
-    MOCK_METHOD(void, requestDisplayMode, (DisplayModePtr, DisplayModeEvent), (override));
+    MOCK_METHOD(void, requestDisplayModes, (std::vector<scheduler::DisplayModeConfig>), (override));
     MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
     MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
     void setVsyncEnabled(bool) override {}
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+    void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
deleted file mode 100644
index 0a0e7b5..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ /dev/null
@@ -1,32 +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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include "mock/MockSurfaceInterceptor.h"
-
-namespace android::mock {
-
-// Explicit default instantiation is recommended.
-SurfaceInterceptor::SurfaceInterceptor() = default;
-SurfaceInterceptor::~SurfaceInterceptor() = default;
-
-} // namespace android::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
deleted file mode 100644
index b085027..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ /dev/null
@@ -1,50 +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 "SurfaceInterceptor.h"
-
-namespace android::mock {
-
-class SurfaceInterceptor : public android::SurfaceInterceptor {
-public:
-    SurfaceInterceptor();
-    ~SurfaceInterceptor() override;
-
-    MOCK_METHOD2(enable,
-                 void(const SortedVector<sp<Layer>>&,
-                      const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
-    MOCK_METHOD0(disable, void());
-    MOCK_METHOD0(isEnabled, bool());
-    MOCK_METHOD1(addTransactionTraceListener, void(const sp<gui::ITransactionTraceListener>&));
-    MOCK_METHOD1(binderDied, void(const wp<IBinder>&));
-    MOCK_METHOD7(saveTransaction,
-                 void(const Vector<ComposerState>&,
-                      const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
-                      const Vector<DisplayState>&, uint32_t, int, int, uint64_t));
-    MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
-    MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
-    MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
-    MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&));
-    MOCK_METHOD1(saveDisplayDeletion, void(int32_t));
-    MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t));
-    MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
-};
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index cb7040a..f297da5 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -18,6 +18,7 @@
 #include <gui/AidlStatusUtil.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <private/gui/ComposerServiceAIDL.h>
+#include <ui/FenceResult.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
 #include <functional>
@@ -46,11 +47,15 @@
             return err;
         }
         captureResults = captureListener->waitForResults();
-        return captureResults.result;
+        return fenceStatus(captureResults.fenceResult);
     }
 
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
-        captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        // TODO(b/248317436): extend to cover all displays for multi-display devices
+        const auto display =
+                ids.empty() ? nullptr : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+        captureScreen(sc, display);
     }
 
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
@@ -80,7 +85,7 @@
             return err;
         }
         captureResults = captureListener->waitForResults();
-        return captureResults.result;
+        return fenceStatus(captureResults.fenceResult);
     }
 
     static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) {
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 2002bdf..5403baf 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -59,6 +59,12 @@
         "-Wunreachable-code",
     ],
 
+    // FIXME: Workaround LTO build breakage
+    // http://b/241699694
+    lto: {
+        never: true,
+    },
+
     local_include_dirs: ["include"],
 
     export_include_dirs: ["include"],
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 5719b5c..a87f82f 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -27,6 +27,9 @@
     symbol_file: "libvulkan.map.txt",
     first_version: "24",
     unversioned_until: "current",
+    export_header_libs: [
+        "ndk_vulkan_headers",
+    ],
 }
 
 cc_library_shared {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 0a33760..abcac3c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -19,6 +19,8 @@
 #include <android/hardware/graphics/common/1.0/types.h>
 #include <grallocusage/GrallocUsageConversion.h>
 #include <graphicsenv/GraphicsEnv.h>
+#include <hardware/gralloc.h>
+#include <hardware/gralloc1.h>
 #include <log/log.h>
 #include <sync/sync.h>
 #include <system/window.h>
@@ -42,6 +44,26 @@
 
 namespace {
 
+static uint64_t convertGralloc1ToBufferUsage(uint64_t producerUsage,
+                                             uint64_t consumerUsage) {
+    static_assert(uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) ==
+                      uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN),
+                  "expected ConsumerUsage and ProducerUsage CPU_READ_OFTEN "
+                  "bits to match");
+    uint64_t merged = producerUsage | consumerUsage;
+    if ((merged & (GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN)) ==
+        GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) {
+        merged &= ~uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN);
+        merged |= BufferUsage::CPU_READ_OFTEN;
+    }
+    if ((merged & (GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN)) ==
+        GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN) {
+        merged &= ~uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN);
+        merged |= BufferUsage::CPU_WRITE_OFTEN;
+    }
+    return merged;
+}
+
 const VkSurfaceTransformFlagsKHR kSupportedTransforms =
     VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR |
     VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR |
@@ -646,10 +668,11 @@
         // VkSurfaceProtectedCapabilitiesKHR::supportsProtected.  The following
         // four values cannot be known without a surface.  Default values will
         // be supplied anyway, but cannot be relied upon.
-        width = 1000;
-        height = 1000;
-        transform_hint = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-        max_buffer_count = 10;
+        width = 0xFFFFFFFF;
+        height = 0xFFFFFFFF;
+        transform_hint = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR;
+        capabilities->minImageCount = 0xFFFFFFFF;
+        capabilities->maxImageCount = 0xFFFFFFFF;
     } else {
         ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
 
@@ -681,9 +704,9 @@
                   strerror(-err), err);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
+        capabilities->minImageCount = std::min(max_buffer_count, 3);
+        capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
     }
-    capabilities->minImageCount = std::min(max_buffer_count, 3);
-    capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
 
     capabilities->currentExtent =
         VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
@@ -763,7 +786,11 @@
     // We must support R8G8B8A8
     std::vector<VkSurfaceFormatKHR> all_formats = {
         {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
-        {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
+        {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
+        // Also allow to use PASS_THROUGH + HAL_DATASPACE_ARBITRARY
+        {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT},
+        {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT},
+    };
 
     if (colorspace_ext) {
         all_formats.emplace_back(VkSurfaceFormatKHR{
@@ -1333,7 +1360,7 @@
         num_images = 1;
     }
 
-    int32_t legacy_usage = 0;
+    uint64_t native_usage = 0;
     if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
         uint64_t consumer_usage, producer_usage;
         ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
@@ -1345,10 +1372,11 @@
             ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
-        legacy_usage =
-            android_convertGralloc1To0Usage(producer_usage, consumer_usage);
+        native_usage =
+            convertGralloc1ToBufferUsage(consumer_usage, producer_usage);
     } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
         ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
+        int32_t legacy_usage = 0;
         result = dispatch.GetSwapchainGrallocUsageANDROID(
             device, create_info->imageFormat, create_info->imageUsage,
             &legacy_usage);
@@ -1357,8 +1385,9 @@
             ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
+        native_usage = static_cast<uint64_t>(legacy_usage);
     }
-    uint64_t native_usage = static_cast<uint64_t>(legacy_usage);
+    native_usage |= surface.consumer_usage;
 
     bool createProtectedSwapchain = false;
     if (create_info->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) {