Merge "Accomodate brightness space change for client target"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index bb1d206..08a3d9a 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -246,7 +246,7 @@
         { OPT,      "events/gpu_mem/gpu_mem_total/enable" },
         { OPT,      "events/fastrpc/fastrpc_dma_stat/enable" },
     } },
-    { "thermal",  "Thermal event", 0, {
+    { "thermal",  "Thermal event", ATRACE_TAG_THERMAL, {
         { REQ,      "events/thermal/thermal_temperature/enable" },
         { OPT,      "events/thermal/cdev_update/enable" },
     } },
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
index c2c8a72..4d3a67b 100644
--- a/cmds/dumpstate/DumpPool.cpp
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -33,6 +33,20 @@
 
 const std::string DumpPool::PREFIX_TMPFILE_NAME = "dump-tmp.";
 
+
+void WaitForTask(std::future<std::string> future, const std::string& title, int out_fd) {
+    DurationReporter duration_reporter("Wait for " + title, true);
+
+    std::string result = future.get();
+    if (result.empty()) {
+        return;
+    }
+    DumpFileToFd(out_fd, title, result);
+    if (unlink(result.c_str())) {
+        MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
+    }
+}
+
 DumpPool::DumpPool(const std::string& tmp_root) : tmp_root_(tmp_root), shutdown_(false),
         log_duration_(true) {
     assert(!tmp_root.empty());
@@ -40,31 +54,10 @@
 }
 
 DumpPool::~DumpPool() {
-    shutdown();
-}
-
-void DumpPool::start(int thread_counts) {
-    assert(thread_counts > 0);
-    assert(threads_.empty());
-    if (thread_counts > MAX_THREAD_COUNT) {
-        thread_counts = MAX_THREAD_COUNT;
-    }
-    MYLOGI("Start thread pool:%d", thread_counts);
-    shutdown_ = false;
-    for (int i = 0; i < thread_counts; i++) {
-        threads_.emplace_back(std::thread([=]() {
-            setThreadName(pthread_self(), i + 1);
-            loop();
-        }));
-    }
-}
-
-void DumpPool::shutdown() {
     std::unique_lock lock(lock_);
     if (shutdown_ || threads_.empty()) {
         return;
     }
-    futures_map_.clear();
     while (!tasks_.empty()) tasks_.pop();
 
     shutdown_ = true;
@@ -76,27 +69,22 @@
     }
     threads_.clear();
     deleteTempFiles(tmp_root_);
-    MYLOGI("shutdown thread pool");
+    MYLOGI("shutdown thread pool\n");
 }
 
-void DumpPool::waitForTask(const std::string& task_name, const std::string& title,
-        int out_fd) {
-    DurationReporter duration_reporter("Wait for " + task_name, true);
-    auto iterator = futures_map_.find(task_name);
-    if (iterator == futures_map_.end()) {
-        MYLOGW("Task %s does not exist", task_name.c_str());
-        return;
+void DumpPool::start(int thread_counts) {
+    assert(thread_counts > 0);
+    assert(threads_.empty());
+    if (thread_counts > MAX_THREAD_COUNT) {
+        thread_counts = MAX_THREAD_COUNT;
     }
-    Future future = iterator->second;
-    futures_map_.erase(iterator);
-
-    std::string result = future.get();
-    if (result.empty()) {
-        return;
-    }
-    DumpFileToFd(out_fd, title, result);
-    if (unlink(result.c_str())) {
-        MYLOGE("Failed to unlink (%s): %s\n", result.c_str(), strerror(errno));
+    MYLOGI("Start thread pool:%d\n", thread_counts);
+    shutdown_ = false;
+    for (int i = 0; i < thread_counts; i++) {
+        threads_.emplace_back(std::thread([=]() {
+            setThreadName(pthread_self(), i + 1);
+            loop();
+        }));
     }
 }
 
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
index 0c3c2cc..9fb0fcc 100644
--- a/cmds/dumpstate/DumpPool.h
+++ b/cmds/dumpstate/DumpPool.h
@@ -18,7 +18,6 @@
 #define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
 
 #include <future>
-#include <map>
 #include <queue>
 #include <string>
 
@@ -32,8 +31,26 @@
 class DumpPoolTest;
 
 /*
+ * Waits until the task is finished. Dumps the task results to the specified
+ * out_fd.
+ *
+ * |future| The task future.
+ * |title| Dump title string to the out_fd, an empty string for nothing.
+ * |out_fd| The target file to dump the result from the task.
+ */
+void WaitForTask(std::future<std::string> future, const std::string& title, int out_fd);
+
+/*
+ * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
+ */
+
+inline void WaitForTask(std::future<std::string> future) {
+    WaitForTask(std::move(future), "", STDOUT_FILENO);
+}
+
+/*
  * A thread pool with the fixed number of threads to execute multiple dump tasks
- * simultaneously for the dumpstate. The dump task is a callable function. It
+ * simultaneously for dumpstate. The dump task is a callable function. It
  * could include a file descriptor as a parameter to redirect dump results, if
  * it needs to output results to the bugreport. This can avoid messing up
  * bugreport's results when multiple dump tasks are running at the same time.
@@ -44,13 +61,16 @@
  * }
  * ...
  * DumpPool pool(tmp_root);
- * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
+ * auto task = pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
  * ...
- * pool.waitForTask("TaskName");
+ * WaitForTask(task);
  *
  * DumpFoo is a callable function included a out_fd parameter. Using the
  * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
  * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
+ *
+ * std::futures returned by `enqueueTask*()` must all have their `get` methods
+ * called, or have been destroyed before the DumpPool itself is destroyed.
  */
 class DumpPool {
   friend class android::os::dumpstate::DumpPoolTest;
@@ -63,6 +83,12 @@
      * files.
      */
     explicit DumpPool(const std::string& tmp_root);
+
+    /*
+     * Will waits until all threads exit the loop. Destroying DumpPool before destroying the
+     * associated std::futures created by `enqueueTask*` will cause an abort on Android because
+     * Android is built with `-fno-exceptions`.
+     */
     ~DumpPool();
 
     /*
@@ -73,68 +99,47 @@
     void start(int thread_counts = MAX_THREAD_COUNT);
 
     /*
-     * Requests to shutdown the pool and waits until all threads exit the loop.
-     */
-    void shutdown();
-
-    /*
      * Adds a task into the queue of the thread pool.
      *
-     * |task_name| The name of the task. It's also the title of the
+     * |duration_title| The name of the task. It's also the title of the
      * DurationReporter log.
      * |f| Callable function to execute the task.
      * |args| A list of arguments.
      *
      * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
      */
-    template<class F, class... Args> void enqueueTask(const std::string& task_name, F&& f,
-            Args&&... args) {
+    template<class F, class... Args>
+    std::future<std::string> enqueueTask(const std::string& duration_title, F&& f, Args&&... args) {
         std::function<void(void)> func = std::bind(std::forward<F>(f),
                 std::forward<Args>(args)...);
-        futures_map_[task_name] = post(task_name, func);
+        auto future = post(duration_title, func);
         if (threads_.empty()) {
             start();
         }
+        return future;
     }
 
     /*
      * Adds a task into the queue of the thread pool. The task takes a file
      * descriptor as a parameter to redirect dump results to a temporary file.
      *
-     * |task_name| The name of the task. It's also the title of the
-     * DurationReporter log.
+     * |duration_title| The title of the DurationReporter log.
      * |f| Callable function to execute the task.
      * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
      * argument needs to be included here.
      */
-    template<class F, class... Args> void enqueueTaskWithFd(const std::string& task_name, F&& f,
-            Args&&... args) {
+    template<class F, class... Args> std::future<std::string> enqueueTaskWithFd(
+            const std::string& duration_title, F&& f, Args&&... args) {
         std::function<void(int)> func = std::bind(std::forward<F>(f),
                 std::forward<Args>(args)...);
-        futures_map_[task_name] = post(task_name, func);
+        auto future = post(duration_title, func);
         if (threads_.empty()) {
             start();
         }
+        return future;
     }
 
     /*
-     * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
-     */
-    void waitForTask(const std::string& task_name) {
-        waitForTask(task_name, "", STDOUT_FILENO);
-    }
-
-    /*
-     * Waits until the task is finished. Dumps the task results to the specified
-     * out_fd.
-     *
-     * |task_name| The name of the task.
-     * |title| Dump title string to the out_fd, an empty string for nothing.
-     * |out_fd| The target file to dump the result from the task.
-     */
-    void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
-
-    /*
      * Deletes temporary files created by DumpPool.
      */
     void deleteTempFiles();
@@ -143,22 +148,22 @@
 
   private:
     using Task = std::packaged_task<std::string()>;
-    using Future = std::shared_future<std::string>;
 
     template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
 
-    template<class T> Future post(const std::string& task_name, T dump_func) {
+    template<class T>
+    std::future<std::string> post(const std::string& duration_title, T dump_func) {
         Task packaged_task([=]() {
             std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
             if (!tmp_file_ptr) {
                 return std::string("");
             }
-            invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
+            invokeTask(dump_func, duration_title, tmp_file_ptr->fd.get());
             fsync(tmp_file_ptr->fd.get());
             return std::string(tmp_file_ptr->path);
         });
         std::unique_lock lock(lock_);
-        auto future = packaged_task.get_future().share();
+        auto future = packaged_task.get_future();
         tasks_.push(std::move(packaged_task));
         condition_variable_.notify_one();
         return future;
@@ -194,7 +199,6 @@
 
     std::vector<std::thread> threads_;
     std::queue<Task> tasks_;
-    std::map<std::string, Future> futures_map_;
 
     DISALLOW_COPY_AND_ASSIGN(DumpPool);
 };
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index c833d0e..aa42541 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -48,11 +48,26 @@
     sigemptyset(&child_mask);
     sigaddset(&child_mask, SIGCHLD);
 
+    // block SIGCHLD before we check if a process has exited
     if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
         printf("*** sigprocmask failed: %s\n", strerror(errno));
         return false;
     }
 
+    // if the child has exited already, handle and reset signals before leaving
+    pid_t child_pid = waitpid(pid, status, WNOHANG);
+    if (child_pid != pid) {
+        if (child_pid > 0) {
+            printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+            sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+            return false;
+        }
+    } else {
+        sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+        return true;
+    }
+
+    // wait for a SIGCHLD
     timespec ts;
     ts.tv_sec = MSEC_TO_SEC(timeout_ms);
     ts.tv_nsec = (timeout_ms % 1000) * 1000000;
@@ -76,7 +91,7 @@
         return false;
     }
 
-    pid_t child_pid = waitpid(pid, status, WNOHANG);
+    child_pid = waitpid(pid, status, WNOHANG);
     if (child_pid != pid) {
         if (child_pid != -1) {
             printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
@@ -232,7 +247,6 @@
             dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(),
                     strerror(err));
         }
-        fsync(out_fd);
         return -1;
     }
     return DumpFileFromFdToFd(title, path, fd.get(), out_fd, PropertiesHelper::IsDryRun());
@@ -280,7 +294,6 @@
 
     if (!title.empty()) {
         dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command);
-        fsync(fd);
     }
 
     const std::string& logging_message = options.LoggingMessage();
@@ -299,14 +312,13 @@
             // There is no title, but we should still print a dry-run message
             dprintf(fd, "%s: skipped on dry run\n", command_string.c_str());
         }
-        fsync(fd);
         return 0;
     }
 
     const char* path = args[0];
 
     uint64_t start = Nanotime();
-    pid_t pid = fork();
+    pid_t pid = vfork();
 
     /* handle error case */
     if (pid < 0) {
@@ -323,7 +335,7 @@
                         strerror(errno));
             }
             MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
-            return -1;
+            _exit(EXIT_FAILURE);
         }
 
         if (options.ShouldCloseAllFileDescriptorsOnExec()) {
@@ -376,7 +388,6 @@
     /* handle parent case */
     int status;
     bool ret = waitpid_with_timeout(pid, options.TimeoutInMs(), &status);
-    fsync(fd);
 
     uint64_t elapsed = Nanotime() - start;
     if (!ret) {
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8ccd940..0e9ce89 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -120,6 +120,7 @@
 using android::os::dumpstate::DumpPool;
 using android::os::dumpstate::PropertiesHelper;
 using android::os::dumpstate::TaskQueue;
+using android::os::dumpstate::WaitForTask;
 
 // Keep in sync with
 // frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -218,9 +219,9 @@
     RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__);               \
     RETURN_IF_USER_DENIED_CONSENT();
 
-#define WAIT_TASK_WITH_CONSENT_CHECK(task_name, pool_ptr) \
+#define WAIT_TASK_WITH_CONSENT_CHECK(future) \
     RETURN_IF_USER_DENIED_CONSENT();                      \
-    pool_ptr->waitForTask(task_name);                     \
+    WaitForTask(future);                     \
     RETURN_IF_USER_DENIED_CONSENT();
 
 static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
@@ -1549,15 +1550,18 @@
     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;
     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.
         ds.dump_pool_->start(/* thread_counts = */2);
 
-        ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
-        ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
-        ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
-        ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
+        dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+        dump_incident_report = ds.dump_pool_->enqueueTask(
+            DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
+        dump_board = ds.dump_pool_->enqueueTaskWithFd(
+            DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+        dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
     }
 
     // Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1582,7 +1586,6 @@
     DumpFile("BUDDYINFO", "/proc/buddyinfo");
     DumpExternalFragmentationInfo();
 
-    DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources");
     DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
 
     RunCommand("PROCESSES AND THREADS",
@@ -1592,7 +1595,7 @@
                                          CommandOptions::AS_ROOT);
 
     if (ds.dump_pool_) {
-        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_HALS_TASK, ds.dump_pool_);
+        WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_hals));
     } else {
         RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_HALS_TASK, DumpHals);
     }
@@ -1689,7 +1692,7 @@
     ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
 
     if (ds.dump_pool_) {
-        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_BOARD_TASK, ds.dump_pool_);
+        WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_board));
     } else {
         RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
     }
@@ -1718,7 +1721,7 @@
     ds.AddDir("/data/misc/bluetooth/logs", true);
 
     if (ds.dump_pool_) {
-        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
+        WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_checkins));
     } else {
         RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
     }
@@ -1752,7 +1755,7 @@
     dump_frozen_cgroupfs();
 
     if (ds.dump_pool_) {
-        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, 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,
                 DumpIncidentReport);
@@ -1777,6 +1780,7 @@
     time_t logcat_ts = time(nullptr);
 
     /* collect stack traces from Dalvik and native processes (needs root) */
+    std::future<std::string> dump_traces;
     if (dump_pool_) {
         RETURN_IF_USER_DENIED_CONSENT();
         // One thread is enough since we only need to enqueue DumpTraces here.
@@ -1784,7 +1788,8 @@
 
         // DumpTraces takes long time, post it to the another thread in the
         // pool, if pool is available
-        dump_pool_->enqueueTask(DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path);
+        dump_traces = dump_pool_->enqueueTask(
+            DUMP_TRACES_TASK, &Dumpstate::DumpTraces, &ds, &dump_traces_path);
     } else {
         RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces,
                 &dump_traces_path);
@@ -1833,12 +1838,11 @@
 
     if (dump_pool_) {
         RETURN_IF_USER_DENIED_CONSENT();
-        dump_pool_->waitForTask(DUMP_TRACES_TASK);
+        WaitForTask(std::move(dump_traces));
 
-        // Current running thread in the pool is the root user also. Shutdown
-        // the pool and restart later to ensure all threads in the pool could
-        // drop the root user.
-        dump_pool_->shutdown();
+        // Current running thread in the pool is the root user also. Delete
+        // the pool and make a new one later to ensure none of threads in the pool are root.
+        dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
     }
     if (!DropRootUser()) {
         return Dumpstate::RunStatus::ERROR;
@@ -1869,8 +1873,9 @@
     } else {
         // DumpHals takes long time, post it to the another thread in the pool,
         // if pool is available.
+        std::future<std::string> dump_hals;
         if (ds.dump_pool_) {
-            ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+            dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
         }
         // Contains various system properties and process startup info.
         do_dmesg();
@@ -1880,7 +1885,7 @@
         DoKmsg();
         // DumpHals contains unrelated hardware info (camera, NFC, biometrics, ...).
         if (ds.dump_pool_) {
-            ds.dump_pool_->waitForTask(DUMP_HALS_TASK);
+            WaitForTask(std::move(dump_hals));
         } else {
             RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals);
         }
@@ -1914,12 +1919,14 @@
 
     // Starts thread pool after the root user is dropped, and two additional threads
     // are created for DumpHals in the DumpstateRadioCommon and DumpstateBoard.
+    std::future<std::string> dump_board;
     if (ds.dump_pool_) {
         ds.dump_pool_->start(/*thread_counts =*/2);
 
         // DumpstateBoard takes long time, post it to the another thread in the pool,
         // if pool is available.
-        ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+        dump_board = ds.dump_pool_->enqueueTaskWithFd(
+            DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
     }
 
     DumpstateRadioCommon(include_sensitive_info);
@@ -2002,7 +2009,7 @@
     printf("========================================================\n");
 
     if (ds.dump_pool_) {
-        ds.dump_pool_->waitForTask(DUMP_BOARD_TASK);
+        WaitForTask(std::move(dump_board));
     } else {
         RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
     }
@@ -3226,8 +3233,7 @@
 
 void Dumpstate::ShutdownDumpPool() {
     if (dump_pool_) {
-        dump_pool_->shutdown();
-        dump_pool_ = nullptr;
+        dump_pool_.reset();
     }
     if (zip_entry_tasks_) {
         zip_entry_tasks_->run(/* do_cancel = */true);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 4a99cd8..ee6b1ae 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -478,7 +478,7 @@
     // This is useful for debugging.
     std::string log_path_;
 
-    // Full path of the bugreport file, be it zip or text, inside bugreport_internal_dir_.
+    // Full path of the bugreport zip file inside bugreport_internal_dir_.
     std::string path_;
 
     // Full path of the file containing the screenshot (when requested).
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 42beb2b..70b4e5c 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1720,14 +1720,13 @@
         dprintf(out_fd, "C");
     };
     setLogDuration(/* log_duration = */false);
-    dump_pool_->enqueueTaskWithFd(/* task_name = */"1", dump_func_1, std::placeholders::_1);
-    dump_pool_->enqueueTaskWithFd(/* task_name = */"2", dump_func_2, std::placeholders::_1);
-    dump_pool_->enqueueTaskWithFd(/* task_name = */"3", dump_func_3, std::placeholders::_1);
+    auto t1 = dump_pool_->enqueueTaskWithFd("", dump_func_1, std::placeholders::_1);
+    auto t2 = dump_pool_->enqueueTaskWithFd("", dump_func_2, std::placeholders::_1);
+    auto t3 = dump_pool_->enqueueTaskWithFd("", dump_func_3, std::placeholders::_1);
 
-    dump_pool_->waitForTask("1", "", out_fd_.get());
-    dump_pool_->waitForTask("2", "", out_fd_.get());
-    dump_pool_->waitForTask("3", "", out_fd_.get());
-    dump_pool_->shutdown();
+    WaitForTask(std::move(t1), "", out_fd_.get());
+    WaitForTask(std::move(t2), "", out_fd_.get());
+    WaitForTask(std::move(t3), "", out_fd_.get());
 
     std::string result;
     ReadFileToString(out_path_, &result);
@@ -1741,9 +1740,8 @@
         run_1 = true;
     };
 
-    dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
-    dump_pool_->waitForTask("1", "", out_fd_.get());
-    dump_pool_->shutdown();
+    auto t1 = dump_pool_->enqueueTask(/* duration_title = */"1", dump_func_1);
+    WaitForTask(std::move(t1), "", out_fd_.get());
 
     std::string result;
     ReadFileToString(out_path_, &result);
@@ -1752,27 +1750,6 @@
     EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
 }
 
-TEST_F(DumpPoolTest, Shutdown_withoutCrash) {
-    bool run_1 = false;
-    auto dump_func_1 = [&]() {
-        run_1 = true;
-    };
-    auto dump_func = []() {
-        sleep(1);
-    };
-
-    dump_pool_->start(/* thread_counts = */1);
-    dump_pool_->enqueueTask(/* task_name = */"1", dump_func_1);
-    dump_pool_->enqueueTask(/* task_name = */"2", dump_func);
-    dump_pool_->enqueueTask(/* task_name = */"3", dump_func);
-    dump_pool_->enqueueTask(/* task_name = */"4", dump_func);
-    dump_pool_->waitForTask("1", "", out_fd_.get());
-    dump_pool_->shutdown();
-
-    EXPECT_TRUE(run_1);
-    EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
-}
-
 class TaskQueueTest : public DumpstateBaseTest {
 public:
     void SetUp() {
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 00babc3..c9f680b 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -51,6 +51,7 @@
     ],
     static_libs: [
         "libasync_safe",
+        "libext2_uuid",
     ],
     export_shared_lib_headers: [
         "libbinder",
@@ -262,6 +263,7 @@
         "libasync_safe",
         "libdiskusage",
         "libotapreoptparameters",
+        "libext2_uuid",
     ],
 
     shared_libs: [
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 240e6be..e7bde21 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -18,13 +18,9 @@
 
 #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
 
-#include <algorithm>
 #include <errno.h>
-#include <fstream>
 #include <fts.h>
-#include <functional>
 #include <inttypes.h>
-#include <regex>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -40,6 +36,11 @@
 #include <sys/wait.h>
 #include <sys/xattr.h>
 #include <unistd.h>
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <functional>
+#include <regex>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -103,11 +104,6 @@
 static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
 static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
 
-// fsverity assumes the page size is always 4096. If not, the feature can not be
-// enabled.
-static constexpr int kVerityPageSize = 4096;
-static constexpr size_t kSha256Size = 32;
-static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
 static constexpr const char* kFuseProp = "persist.sys.fuse";
 
 /**
@@ -261,12 +257,6 @@
     }                                                       \
 }
 
-#define ASSERT_PAGE_SIZE_4K() {                             \
-    if (getpagesize() != kVerityPageSize) {                 \
-        return error("FSVerity only supports 4K pages");     \
-    }                                                       \
-}
-
 #ifdef GRANULAR_LOCKS
 
 /**
@@ -731,6 +721,76 @@
             return error("Failed to prepare profiles for " + packageName);
         }
     }
+
+    // TODO(b/220095381): Due to boot time regression, we have omitted call to
+    // createAppDirectoryForSupplementalData from here temporarily (unless it's for testing)
+    if (uuid_ != nullptr && strcmp(uuid_, "TEST") == 0) {
+        auto status = createAppDirectoryForSupplementalData(uuid, packageName, userId, appId,
+                                                            previousAppId, seInfo, flags);
+        if (!status.isOk()) {
+            return status;
+        }
+    }
+
+    return ok();
+}
+
+/**
+ * Responsible for creating /data/user/0/supplemental/<app-name> directory and other
+ * app level sub directories, such as ./shared
+ */
+binder::Status InstalldNativeService::createAppDirectoryForSupplementalData(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t flags) {
+    int32_t supplementalUid = multiuser_get_supplemental_uid(userId, appId);
+    if (supplementalUid == -1) {
+        // There no valid supplemental process for this app. Skip creation of data directory
+        return ok();
+    }
+
+    // TODO(b/211763739): what if uuid is not nullptr or TEST?
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+    for (int i = 0; i < 2; i++) {
+        int currentFlag = storageFlags[i];
+        if ((flags & currentFlag) == 0) {
+            continue;
+        }
+        bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+
+        // /data/misc_{ce,de}/<user-id>/supplemental directory gets created by vold
+        // during user creation
+
+        // Prepare the app directory
+        auto appPath = create_data_misc_supplemental_package_path(uuid_, isCeData, userId,
+                                                                  packageName.c_str());
+        if (prepare_app_dir(appPath, 0751, AID_SYSTEM)) {
+            return error("Failed to prepare " + appPath);
+        }
+
+        // Now prepare the shared directory which will be accessible by all codes
+        auto sharedPath = create_data_misc_supplemental_shared_path(uuid_, isCeData, userId,
+                                                                    packageName.c_str());
+
+        int32_t previousSupplementalUid = multiuser_get_supplemental_uid(userId, previousAppId);
+        int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
+        if (cacheGid == -1) {
+            return exception(binder::Status::EX_ILLEGAL_STATE,
+                             StringPrintf("cacheGid cannot be -1 for supplemental data"));
+        }
+        auto status = createAppDataDirs(sharedPath, supplementalUid, &previousSupplementalUid,
+                                        cacheGid, seInfo, 0700);
+        if (!status.isOk()) {
+            return status;
+        }
+
+        // TODO(b/211763739): We also need to handle art profile creations
+
+        // TODO(b/211763739): And return the CE inode of the supplemental root directory and
+        // app directory under it so we can clear contents while CE storage is locked
+    }
+
     return ok();
 }
 
@@ -968,13 +1028,13 @@
     binder::Status res = ok();
     if (flags & FLAG_STORAGE_CE) {
         auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
-        if (delete_dir_contents_and_dir(path) != 0) {
+        if (rename_delete_dir_contents_and_dir(path) != 0) {
             res = error("Failed to delete " + path);
         }
     }
     if (flags & FLAG_STORAGE_DE) {
         auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
-        if (delete_dir_contents_and_dir(path) != 0) {
+        if (rename_delete_dir_contents_and_dir(path) != 0) {
             res = error("Failed to delete " + path);
         }
         if ((flags & FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) {
@@ -1008,7 +1068,6 @@
             if (delete_dir_contents_and_dir(path, true) != 0) {
                 res = error("Failed to delete contents of " + path);
             }
-
             path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
             if (delete_dir_contents_and_dir(path, true) != 0) {
                 res = error("Failed to delete contents of " + path);
@@ -2972,147 +3031,6 @@
     return *_aidl_return == -1 ? error() : ok();
 }
 
-// This kernel feature is experimental.
-// TODO: remove local definition once upstreamed
-#ifndef FS_IOC_ENABLE_VERITY
-
-#define FS_IOC_ENABLE_VERITY           _IO('f', 133)
-#define FS_IOC_SET_VERITY_MEASUREMENT  _IOW('f', 134, struct fsverity_measurement)
-
-#define FS_VERITY_ALG_SHA256           1
-
-struct fsverity_measurement {
-    __u16 digest_algorithm;
-    __u16 digest_size;
-    __u32 reserved1;
-    __u64 reserved2[3];
-    __u8 digest[];
-};
-
-#endif
-
-binder::Status InstalldNativeService::installApkVerity(const std::string& packageName,
-                                                       const std::string& filePath,
-                                                       android::base::unique_fd verityInputAshmem,
-                                                       int32_t contentSize) {
-    ENFORCE_UID(AID_SYSTEM);
-    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    CHECK_ARGUMENT_PATH(filePath);
-    LOCK_PACKAGE();
-
-    if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
-        return ok();
-    }
-#ifndef NDEBUG
-    ASSERT_PAGE_SIZE_4K();
-#endif
-    // TODO: also check fsverity support in the current file system if compiled with DEBUG.
-    // TODO: change ashmem to some temporary file to support huge apk.
-    if (!ashmem_valid(verityInputAshmem.get())) {
-        return error("FD is not an ashmem");
-    }
-
-    // 1. Seek to the next page boundary beyond the end of the file.
-    ::android::base::unique_fd wfd(open(filePath.c_str(), O_WRONLY));
-    if (wfd.get() < 0) {
-        return error("Failed to open " + filePath);
-    }
-    struct stat st;
-    if (fstat(wfd.get(), &st) < 0) {
-        return error("Failed to stat " + filePath);
-    }
-    // fsverity starts from the block boundary.
-    off_t padding = kVerityPageSize - st.st_size % kVerityPageSize;
-    if (padding == kVerityPageSize) {
-        padding = 0;
-    }
-    if (lseek(wfd.get(), st.st_size + padding, SEEK_SET) < 0) {
-        return error("Failed to lseek " + filePath);
-    }
-
-    // 2. Write everything in the ashmem to the file.  Note that allocated
-    //    ashmem size is multiple of page size, which is different from the
-    //    actual content size.
-    int shmSize = ashmem_get_size_region(verityInputAshmem.get());
-    if (shmSize < 0) {
-        return error("Failed to get ashmem size: " + std::to_string(shmSize));
-    }
-    if (contentSize < 0) {
-        return error("Invalid content size: " + std::to_string(contentSize));
-    }
-    if (contentSize > shmSize) {
-        return error("Content size overflow: " + std::to_string(contentSize) + " > " +
-                     std::to_string(shmSize));
-    }
-    auto data = std::unique_ptr<void, std::function<void (void *)>>(
-        mmap(nullptr, contentSize, PROT_READ, MAP_SHARED, verityInputAshmem.get(), 0),
-        [contentSize] (void* ptr) {
-          if (ptr != MAP_FAILED) {
-            munmap(ptr, contentSize);
-          }
-        });
-
-    if (data.get() == MAP_FAILED) {
-        return error("Failed to mmap the ashmem");
-    }
-    char* cursor = reinterpret_cast<char*>(data.get());
-    int remaining = contentSize;
-    while (remaining > 0) {
-        int ret = TEMP_FAILURE_RETRY(write(wfd.get(), cursor, remaining));
-        if (ret < 0) {
-            return error("Failed to write to " + filePath + " (" + std::to_string(remaining) +
-                         + "/" + std::to_string(contentSize) + ")");
-        }
-        cursor += ret;
-        remaining -= ret;
-    }
-    wfd.reset();
-
-    // 3. Enable fsverity (needs readonly fd. Once it's done, the file becomes immutable.
-    ::android::base::unique_fd rfd(open(filePath.c_str(), O_RDONLY));
-    if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) {
-        return error("Failed to enable fsverity on " + filePath);
-    }
-    return ok();
-}
-
-binder::Status InstalldNativeService::assertFsverityRootHashMatches(
-        const std::string& packageName, const std::string& filePath,
-        const std::vector<uint8_t>& expectedHash) {
-    ENFORCE_UID(AID_SYSTEM);
-    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
-    CHECK_ARGUMENT_PATH(filePath);
-    LOCK_PACKAGE();
-
-    if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
-        return ok();
-    }
-    // TODO: also check fsverity support in the current file system if compiled with DEBUG.
-    if (expectedHash.size() != kSha256Size) {
-        return error("verity hash size should be " + std::to_string(kSha256Size) + " but is " +
-                     std::to_string(expectedHash.size()));
-    }
-
-    ::android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY));
-    if (fd.get() < 0) {
-        return error("Failed to open " + filePath + ": " + strerror(errno));
-    }
-
-    unsigned int buffer_size = sizeof(fsverity_measurement) + kSha256Size;
-    std::vector<char> buffer(buffer_size, 0);
-
-    fsverity_measurement* config = reinterpret_cast<fsverity_measurement*>(buffer.data());
-    config->digest_algorithm = FS_VERITY_ALG_SHA256;
-    config->digest_size = kSha256Size;
-    memcpy(config->digest, expectedHash.data(), kSha256Size);
-    if (ioctl(fd.get(), FS_IOC_SET_VERITY_MEASUREMENT, config) < 0) {
-        // This includes an expected failure case with no FSVerity setup. It normally happens when
-        // the apk does not contains the Merkle tree root hash.
-        return error("Failed to measure fsverity on " + filePath + ": " + strerror(errno));
-    }
-    return ok();  // hashes match
-}
-
 binder::Status InstalldNativeService::reconcileSecondaryDexFile(
         const std::string& dexPath, const std::string& packageName, int32_t uid,
         const std::vector<std::string>& isas, const std::optional<std::string>& volumeUuid,
@@ -3342,5 +3260,22 @@
     return ok();
 }
 
+binder::Status InstalldNativeService::cleanupInvalidPackageDirs(
+        const std::optional<std::string>& uuid, int32_t userId, int32_t flags) {
+    const char* uuid_cstr = uuid ? uuid->c_str() : nullptr;
+
+    if (flags & FLAG_STORAGE_CE) {
+        auto ce_path = create_data_user_ce_path(uuid_cstr, userId);
+        cleanup_invalid_package_dirs_under_path(ce_path);
+    }
+
+    if (flags & FLAG_STORAGE_DE) {
+        auto de_path = create_data_user_de_path(uuid_cstr, userId);
+        cleanup_invalid_package_dirs_under_path(de_path);
+    }
+
+    return ok();
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 04662ea..6b4ba1f 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -47,14 +47,9 @@
             int32_t flags);
 
     binder::Status createAppData(const std::optional<std::string>& uuid,
-            const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
-            int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
-            int64_t* _aidl_return);
-    binder::Status createAppDataLocked(const std::optional<std::string>& uuid,
-                                       const std::string& packageName, int32_t userId,
-                                       int32_t flags, int32_t appId, int32_t previousAppId,
-                                       const std::string& seInfo, int32_t targetSdkVersion,
-                                       int64_t* _aidl_return);
+                                 const std::string& packageName, int32_t userId, int32_t flags,
+                                 int32_t appId, int32_t previousAppId, const std::string& seInfo,
+                                 int32_t targetSdkVersion, int64_t* _aidl_return);
 
     binder::Status createAppData(
             const android::os::CreateAppDataArgs& args,
@@ -165,11 +160,6 @@
     binder::Status deleteOdex(const std::string& packageName, const std::string& apkPath,
                               const std::string& instructionSet,
                               const std::optional<std::string>& outputPath, int64_t* _aidl_return);
-    binder::Status installApkVerity(const std::string& packageName, const std::string& filePath,
-                                    android::base::unique_fd verityInput, int32_t contentSize);
-    binder::Status assertFsverityRootHashMatches(const std::string& packageName,
-                                                 const std::string& filePath,
-                                                 const std::vector<uint8_t>& expectedHash);
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
         const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
         const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
@@ -190,6 +180,9 @@
 
     binder::Status migrateLegacyObbData();
 
+    binder::Status cleanupInvalidPackageDirs(const std::optional<std::string>& uuid, int32_t userId,
+                                             int32_t flags);
+
 private:
     std::recursive_mutex mLock;
     std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock;
@@ -205,6 +198,18 @@
     std::unordered_map<uid_t, int64_t> mCacheQuotas;
 
     std::string findDataMediaPath(const std::optional<std::string>& uuid, userid_t userid);
+
+    binder::Status createAppDataLocked(const std::optional<std::string>& uuid,
+                                       const std::string& packageName, int32_t userId,
+                                       int32_t flags, int32_t appId, int32_t previousAppId,
+                                       const std::string& seInfo, int32_t targetSdkVersion,
+                                       int64_t* _aidl_return);
+
+    binder::Status createAppDirectoryForSupplementalData(const std::optional<std::string>& uuid,
+                                                         const std::string& packageName,
+                                                         int32_t userId, int32_t appId,
+                                                         int32_t previousAppId,
+                                                         const std::string& seInfo, int32_t flags);
 };
 
 }  // namespace installd
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 684a311..afedcc6 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -97,10 +97,6 @@
             @utf8InCpp String instructionSet, @utf8InCpp String outputPath);
     long deleteOdex(@utf8InCpp String packageName, @utf8InCpp String apkPath,
             @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath);
-    void installApkVerity(@utf8InCpp String packageName, @utf8InCpp String filePath,
-            in FileDescriptor verityInput, int contentSize);
-    void assertFsverityRootHashMatches(@utf8InCpp String packageName, @utf8InCpp String filePath,
-            in byte[] expectedHash);
 
     boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
         int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
@@ -130,6 +126,8 @@
 
     void migrateLegacyObbData();
 
+    void cleanupInvalidPackageDirs(@nullable @utf8InCpp String uuid, int userId, int flags);
+
     const int FLAG_STORAGE_DE = 0x1;
     const int FLAG_STORAGE_CE = 0x2;
     const int FLAG_STORAGE_EXTERNAL = 0x4;
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 51f7716..e390bab 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -25,6 +25,7 @@
     static_libs: [
         "libasync_safe",
         "libdiskusage",
+        "libext2_uuid",
         "libinstalld",
         "liblog",
     ],
@@ -53,6 +54,7 @@
     static_libs: [
         "libasync_safe",
         "libdiskusage",
+        "libext2_uuid",
         "libinstalld",
         "libziparchive",
         "liblog",
@@ -99,6 +101,7 @@
     static_libs: [
         "libasync_safe",
         "libdiskusage",
+        "libext2_uuid",
         "libinstalld",
         "libziparchive",
         "liblog",
@@ -144,6 +147,7 @@
     static_libs: [
         "libasync_safe",
         "libdiskusage",
+        "libext2_uuid",
         "libinstalld",
         "liblog",
         "liblogwrap",
@@ -204,6 +208,7 @@
         "libutils",
     ],
     static_libs: [
+        "libext2_uuid",
         "libinstalld",
         "liblog",
     ],
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index b831515..703a096 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -74,7 +74,14 @@
 }
 namespace installd {
 
-constexpr const char* kTestUuid = "TEST";
+static constexpr const char* kTestUuid = "TEST";
+static constexpr const char* kTestPath = "/data/local/tmp/user/0";
+static constexpr const uid_t kSystemUid = 1000;
+static constexpr const int32_t kTestUserId = 0;
+static constexpr const uid_t kTestAppId = 19999;
+
+const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId);
+const uid_t kTestAppSupplementalUid = multiuser_get_supplemental_uid(kTestUserId, kTestAppId);
 
 #define FLAG_FORCE InstalldNativeService::FLAG_FORCE
 
@@ -97,7 +104,7 @@
 }
 
 static std::string get_full_path(const char* path) {
-    return StringPrintf("/data/local/tmp/user/0/%s", path);
+    return StringPrintf("%s/%s", kTestPath, path);
 }
 
 static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
@@ -107,12 +114,16 @@
     EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0);
 }
 
-static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
+static int create(const char* path, uid_t owner, gid_t group, mode_t mode) {
     int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
     EXPECT_NE(fd, -1);
     EXPECT_EQ(::fchown(fd, owner, group), 0);
     EXPECT_EQ(::fchmod(fd, mode), 0);
-    EXPECT_EQ(::close(fd), 0);
+    return fd;
+}
+
+static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
+    EXPECT_EQ(::close(create(path, owner, group, mode)), 0);
 }
 
 static int stat_gid(const char* path) {
@@ -127,6 +138,35 @@
     return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
 }
 
+static bool exists(const char* path) {
+    return ::access(get_full_path(path).c_str(), F_OK) == 0;
+}
+
+template <class Pred>
+static bool find_file(const char* path, Pred&& pred) {
+    bool result = false;
+    auto d = opendir(path);
+    if (d == nullptr) {
+        return result;
+    }
+    struct dirent* de;
+    while ((de = readdir(d))) {
+        const char* name = de->d_name;
+        if (pred(name, de->d_type == DT_DIR)) {
+            result = true;
+            break;
+        }
+    }
+    closedir(d);
+    return result;
+}
+
+static bool exists_renamed_deleted_dir() {
+    return find_file(kTestPath, [](const std::string& name, bool is_dir) {
+        return is_dir && is_renamed_deleted_dir(name);
+    });
+}
+
 class ServiceTest : public testing::Test {
 protected:
     InstalldNativeService* service;
@@ -193,6 +233,134 @@
     EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
 }
 
+TEST_F(ServiceTest, DestroyUserData) {
+    LOG(INFO) << "DestroyUserData";
+
+    mkdir("com.example", 10000, 10000, 0700);
+    mkdir("com.example/foo", 10000, 10000, 0700);
+    touch("com.example/foo/file", 10000, 20000, 0700);
+    mkdir("com.example/bar", 10000, 20000, 0700);
+    touch("com.example/bar/file", 10000, 20000, 0700);
+
+    EXPECT_TRUE(exists("com.example/foo"));
+    EXPECT_TRUE(exists("com.example/foo/file"));
+    EXPECT_TRUE(exists("com.example/bar"));
+    EXPECT_TRUE(exists("com.example/bar/file"));
+
+    service->destroyUserData(testUuid, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE);
+
+    EXPECT_FALSE(exists("com.example/foo"));
+    EXPECT_FALSE(exists("com.example/foo/file"));
+    EXPECT_FALSE(exists("com.example/bar"));
+    EXPECT_FALSE(exists("com.example/bar/file"));
+
+    EXPECT_FALSE(exists_renamed_deleted_dir());
+}
+
+TEST_F(ServiceTest, DestroyAppData) {
+    LOG(INFO) << "DestroyAppData";
+
+    mkdir("com.example", 10000, 10000, 0700);
+    mkdir("com.example/foo", 10000, 10000, 0700);
+    touch("com.example/foo/file", 10000, 20000, 0700);
+    mkdir("com.example/bar", 10000, 20000, 0700);
+    touch("com.example/bar/file", 10000, 20000, 0700);
+
+    EXPECT_TRUE(exists("com.example/foo"));
+    EXPECT_TRUE(exists("com.example/foo/file"));
+    EXPECT_TRUE(exists("com.example/bar"));
+    EXPECT_TRUE(exists("com.example/bar/file"));
+
+    service->destroyAppData(testUuid, "com.example", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, 0);
+
+    EXPECT_FALSE(exists("com.example/foo"));
+    EXPECT_FALSE(exists("com.example/foo/file"));
+    EXPECT_FALSE(exists("com.example/bar"));
+    EXPECT_FALSE(exists("com.example/bar/file"));
+
+    EXPECT_FALSE(exists_renamed_deleted_dir());
+}
+
+TEST_F(ServiceTest, CleanupInvalidPackageDirs) {
+    LOG(INFO) << "CleanupInvalidPackageDirs";
+
+    mkdir("5b14b6458a44==deleted==", 10000, 10000, 0700);
+    mkdir("5b14b6458a44==deleted==/foo", 10000, 10000, 0700);
+    touch("5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700);
+    mkdir("5b14b6458a44==deleted==/bar", 10000, 20000, 0700);
+    touch("5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700);
+
+    auto fd = create("5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700);
+
+    mkdir("b14b6458a44NOTdeleted", 10000, 10000, 0700);
+    mkdir("b14b6458a44NOTdeleted/foo", 10000, 10000, 0700);
+    touch("b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700);
+    mkdir("b14b6458a44NOTdeleted/bar", 10000, 20000, 0700);
+    touch("b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700);
+
+    mkdir("com.example", 10000, 10000, 0700);
+    mkdir("com.example/foo", 10000, 10000, 0700);
+    touch("com.example/foo/file", 10000, 20000, 0700);
+    mkdir("com.example/bar", 10000, 20000, 0700);
+    touch("com.example/bar/file", 10000, 20000, 0700);
+
+    mkdir("==deleted==", 10000, 10000, 0700);
+    mkdir("==deleted==/foo", 10000, 10000, 0700);
+    touch("==deleted==/foo/file", 10000, 20000, 0700);
+    mkdir("==deleted==/bar", 10000, 20000, 0700);
+    touch("==deleted==/bar/file", 10000, 20000, 0700);
+
+    EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo"));
+    EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo/file"));
+    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar"));
+    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/file"));
+    EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+
+    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
+    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
+    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
+    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+
+    EXPECT_TRUE(exists("com.example/foo"));
+    EXPECT_TRUE(exists("com.example/foo/file"));
+    EXPECT_TRUE(exists("com.example/bar"));
+    EXPECT_TRUE(exists("com.example/bar/file"));
+
+    EXPECT_TRUE(exists("==deleted==/foo"));
+    EXPECT_TRUE(exists("==deleted==/foo/file"));
+    EXPECT_TRUE(exists("==deleted==/bar"));
+    EXPECT_TRUE(exists("==deleted==/bar/file"));
+
+    EXPECT_TRUE(exists_renamed_deleted_dir());
+
+    service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE);
+
+    EXPECT_EQ(::close(fd), 0);
+
+    EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo"));
+    EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo/file"));
+    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar"));
+    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/file"));
+    EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/opened_file"));
+
+    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo"));
+    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file"));
+    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar"));
+    EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file"));
+
+    EXPECT_TRUE(exists("com.example/foo"));
+    EXPECT_TRUE(exists("com.example/foo/file"));
+    EXPECT_TRUE(exists("com.example/bar"));
+    EXPECT_TRUE(exists("com.example/bar/file"));
+
+    EXPECT_FALSE(exists("==deleted==/foo"));
+    EXPECT_FALSE(exists("==deleted==/foo/file"));
+    EXPECT_FALSE(exists("==deleted==/bar"));
+    EXPECT_FALSE(exists("==deleted==/bar/file"));
+
+    EXPECT_FALSE(exists_renamed_deleted_dir());
+}
+
 TEST_F(ServiceTest, HashSecondaryDex) {
     LOG(INFO) << "HashSecondaryDex";
 
@@ -768,5 +936,120 @@
           "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE));
 }
 
+class AppSupplementalDataTest : public testing::Test {
+public:
+    void CheckFileAccess(const std::string& path, uid_t uid, mode_t mode) {
+        const auto fullPath = "/data/local/tmp/" + path;
+        ASSERT_TRUE(exists(fullPath.c_str())) << "For path: " << fullPath;
+        struct stat st;
+        ASSERT_EQ(0, stat(fullPath.c_str(), &st));
+        ASSERT_EQ(uid, st.st_uid) << "For path: " << fullPath;
+        ASSERT_EQ(uid, st.st_gid) << "For path: " << fullPath;
+        ASSERT_EQ(mode, st.st_mode) << "For path: " << fullPath;
+    }
+
+    bool exists(const char* path) { return ::access(path, F_OK) == 0; }
+
+    // Creates a default CreateAppDataArgs object
+    android::os::CreateAppDataArgs createAppDataArgs() {
+        android::os::CreateAppDataArgs args;
+        args.uuid = kTestUuid;
+        args.packageName = "com.foo";
+        args.userId = kTestUserId;
+        args.appId = kTestAppId;
+        args.seInfo = "default";
+        args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+        return args;
+    }
+
+protected:
+    InstalldNativeService* service;
+
+    virtual void SetUp() {
+        setenv("ANDROID_LOG_TAGS", "*:v", 1);
+        android::base::InitLogging(nullptr);
+
+        service = new InstalldNativeService();
+        clearAppData();
+        ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700));
+        ASSERT_TRUE(mkdirs("/data/local/tmp/user_de/0", 0700));
+        ASSERT_TRUE(mkdirs("/data/local/tmp/misc_ce/0/supplemental", 0700));
+        ASSERT_TRUE(mkdirs("/data/local/tmp/misc_de/0/supplemental", 0700));
+
+        init_globals_from_data_and_root();
+    }
+
+    virtual void TearDown() {
+        delete service;
+        clearAppData();
+    }
+
+private:
+    void clearAppData() {
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_ce", true));
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_de", true));
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
+    }
+};
+
+TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs();
+    args.packageName = "com.foo";
+    args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+
+    CheckFileAccess("misc_ce/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared", kTestAppSupplementalUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/cache", kTestAppSupplementalUid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    CheckFileAccess("misc_de/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess("misc_de/0/supplemental/com.foo/shared", kTestAppSupplementalUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_de/0/supplemental/com.foo/shared/cache", kTestAppSupplementalUid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess("misc_de/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
+                    S_IFDIR | S_ISGID | 0771);
+}
+
+TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs();
+    args.packageName = "com.foo";
+    args.flags = FLAG_STORAGE_CE;
+
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+
+    // Only CE paths should exist
+    CheckFileAccess("misc_ce/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+
+    // DE paths should not exist
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/supplemental/com.foo"));
+}
+
+TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutCeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs();
+    args.packageName = "com.foo";
+    args.flags = FLAG_STORAGE_DE;
+
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+
+    // CE paths should not exist
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/supplemental/com.foo"));
+
+    // Only DE paths should exist
+    CheckFileAccess("misc_de/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index ed87b67..a7a8624 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -555,6 +555,24 @@
     EXPECT_EQ(0, MatchExtension("docx"));
 }
 
+TEST_F(UtilsTest, TestIsRenamedDeletedDir) {
+    EXPECT_FALSE(is_renamed_deleted_dir(""));
+    EXPECT_FALSE(is_renamed_deleted_dir("1"));
+    EXPECT_FALSE(is_renamed_deleted_dir("="));
+    EXPECT_FALSE(is_renamed_deleted_dir("=="));
+    EXPECT_FALSE(is_renamed_deleted_dir("d=="));
+    EXPECT_FALSE(is_renamed_deleted_dir("ed=="));
+    EXPECT_FALSE(is_renamed_deleted_dir("ted=="));
+    EXPECT_FALSE(is_renamed_deleted_dir("eted=="));
+    EXPECT_FALSE(is_renamed_deleted_dir("leted=="));
+    EXPECT_FALSE(is_renamed_deleted_dir("eleted=="));
+    EXPECT_FALSE(is_renamed_deleted_dir("deleted=="));
+    EXPECT_FALSE(is_renamed_deleted_dir("=deleted=="));
+    EXPECT_TRUE(is_renamed_deleted_dir("==deleted=="));
+    EXPECT_TRUE(is_renamed_deleted_dir("123==deleted=="));
+    EXPECT_TRUE(is_renamed_deleted_dir("5b14b6458a44==deleted=="));
+}
+
 TEST_F(UtilsTest, TestRollbackPaths) {
     EXPECT_EQ("/data/misc_ce/0/rollback/239/com.foo",
             create_data_misc_ce_rollback_package_path(nullptr, 0, 239, "com.foo"));
@@ -638,5 +656,39 @@
     ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700));
 }
 
+TEST_F(UtilsTest, TestSupplementalDataPaths) {
+    // Ce data paths
+    EXPECT_EQ("/data/misc_ce/0/supplemental",
+              create_data_misc_supplemental_path(nullptr, /*isCeData=*/true, 0));
+    EXPECT_EQ("/data/misc_ce/10/supplemental",
+              create_data_misc_supplemental_path(nullptr, true, 10));
+
+    EXPECT_EQ("/data/misc_ce/0/supplemental/com.foo",
+              create_data_misc_supplemental_package_path(nullptr, true, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/supplemental/com.foo",
+              create_data_misc_supplemental_package_path(nullptr, true, 10, "com.foo"));
+
+    EXPECT_EQ("/data/misc_ce/0/supplemental/com.foo/shared",
+              create_data_misc_supplemental_shared_path(nullptr, true, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/supplemental/com.foo/shared",
+              create_data_misc_supplemental_shared_path(nullptr, true, 10, "com.foo"));
+
+    // De data paths
+    EXPECT_EQ("/data/misc_de/0/supplemental",
+              create_data_misc_supplemental_path(nullptr, /*isCeData=*/false, 0));
+    EXPECT_EQ("/data/misc_de/10/supplemental",
+              create_data_misc_supplemental_path(nullptr, false, 10));
+
+    EXPECT_EQ("/data/misc_de/0/supplemental/com.foo",
+              create_data_misc_supplemental_package_path(nullptr, false, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/supplemental/com.foo",
+              create_data_misc_supplemental_package_path(nullptr, false, 10, "com.foo"));
+
+    EXPECT_EQ("/data/misc_de/0/supplemental/com.foo/shared",
+              create_data_misc_supplemental_shared_path(nullptr, false, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/supplemental/com.foo/shared",
+              create_data_misc_supplemental_shared_path(nullptr, false, 10, "com.foo"));
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 0f8a732..3ce4b67 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -22,9 +22,10 @@
 #include <stdlib.h>
 #include <sys/capability.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/wait.h>
 #include <sys/xattr.h>
-#include <sys/statvfs.h>
+#include <uuid/uuid.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -47,6 +48,7 @@
 
 #define DEBUG_XATTRS 0
 
+using android::base::Dirname;
 using android::base::EndsWith;
 using android::base::Fdopendir;
 using android::base::StringPrintf;
@@ -55,6 +57,10 @@
 namespace android {
 namespace installd {
 
+using namespace std::literals;
+
+static constexpr auto deletedSuffix = "==deleted=="sv;
+
 /**
  * Check that given string is valid filename, and that it attempts no
  * parent or child directory traversal.
@@ -188,6 +194,43 @@
     return StringPrintf("%s/user_de/%u", data.c_str(), userid);
 }
 
+/**
+ * Create the path name where supplemental data for all apps will be stored.
+ * E.g. /data/misc_ce/0/supplemental
+ */
+std::string create_data_misc_supplemental_path(const char* uuid, bool isCeData, userid_t user) {
+    std::string data(create_data_path(uuid));
+    if (isCeData) {
+        return StringPrintf("%s/misc_ce/%d/supplemental", data.c_str(), user);
+    } else {
+        return StringPrintf("%s/misc_de/%d/supplemental", data.c_str(), user);
+    }
+}
+
+/**
+ * Create the path name where code data for all codes in a particular app will be stored.
+ * E.g. /data/misc_ce/0/supplemental/<app-name>
+ */
+std::string create_data_misc_supplemental_package_path(const char* volume_uuid, bool isCeData,
+                                                       userid_t user, const char* package_name) {
+    check_package_name(package_name);
+    return StringPrintf("%s/%s",
+                        create_data_misc_supplemental_path(volume_uuid, isCeData, user).c_str(),
+                        package_name);
+}
+
+/**
+ * Create the path name where shared code data for a particular app will be stored.
+ * E.g. /data/misc_ce/0/supplemental/<app-name>/shared
+ */
+std::string create_data_misc_supplemental_shared_path(const char* volume_uuid, bool isCeData,
+                                                      userid_t user, const char* package_name) {
+    return StringPrintf("%s/shared",
+                        create_data_misc_supplemental_package_path(volume_uuid, isCeData, user,
+                                                                   package_name)
+                                .c_str());
+}
+
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) {
     return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user);
 }
@@ -595,6 +638,97 @@
     return res;
 }
 
+static std::string make_unique_name(std::string_view suffix) {
+    static constexpr auto uuidStringSize = 36;
+
+    uuid_t guid;
+    uuid_generate(guid);
+
+    std::string name;
+    const auto suffixSize = suffix.size();
+    name.reserve(uuidStringSize + suffixSize);
+
+    name.resize(uuidStringSize);
+    uuid_unparse(guid, name.data());
+    name.append(suffix);
+
+    return name;
+}
+
+static int rename_delete_dir_contents(const std::string& pathname,
+                                      int (*exclusion_predicate)(const char*, const int),
+                                      bool ignore_if_missing) {
+    auto temp_dir_name = make_unique_name(deletedSuffix);
+    auto temp_dir_path =
+            base::StringPrintf("%s/%s", Dirname(pathname).c_str(), temp_dir_name.c_str());
+
+    if (::rename(pathname.c_str(), temp_dir_path.c_str())) {
+        if (ignore_if_missing && (errno == ENOENT)) {
+            return 0;
+        }
+        ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), temp_dir_path.c_str(),
+              strerror(errno));
+        return -errno;
+    }
+
+    return delete_dir_contents(temp_dir_path.c_str(), 1, exclusion_predicate, ignore_if_missing);
+}
+
+bool is_renamed_deleted_dir(const std::string& path) {
+    if (path.size() < deletedSuffix.size()) {
+        return false;
+    }
+    std::string_view pathSuffix{path.c_str() + path.size() - deletedSuffix.size()};
+    return pathSuffix == deletedSuffix;
+}
+
+int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing) {
+    return rename_delete_dir_contents(pathname, nullptr, ignore_if_missing);
+}
+
+static auto open_dir(const char* dir) {
+    struct DirCloser {
+        void operator()(DIR* d) const noexcept { ::closedir(d); }
+    };
+    return std::unique_ptr<DIR, DirCloser>(::opendir(dir));
+}
+
+void cleanup_invalid_package_dirs_under_path(const std::string& pathname) {
+    auto dir = open_dir(pathname.c_str());
+    if (!dir) {
+        return;
+    }
+    int dfd = dirfd(dir.get());
+    if (dfd < 0) {
+        ALOGE("Couldn't dirfd %s: %s\n", pathname.c_str(), strerror(errno));
+        return;
+    }
+
+    struct dirent* de;
+    while ((de = readdir(dir.get()))) {
+        if (de->d_type != DT_DIR) {
+            continue;
+        }
+
+        std::string name{de->d_name};
+        // always skip "." and ".."
+        if (name == "." || name == "..") {
+            continue;
+        }
+
+        if (is_renamed_deleted_dir(name) || !is_valid_filename(name) ||
+            !is_valid_package_name(name)) {
+            ALOGI("Deleting renamed or invalid data directory: %s\n", name.c_str());
+            // Deleting the content.
+            delete_dir_contents_fd(dfd, name.c_str());
+            // Deleting the directory
+            if (unlinkat(dfd, name.c_str(), AT_REMOVEDIR) < 0) {
+                ALOGE("Couldn't unlinkat %s: %s\n", name.c_str(), strerror(errno));
+            }
+        }
+    }
+}
+
 int delete_dir_contents_fd(int dfd, const char *name)
 {
     int fd, res;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 549fc6c..2db1623 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -60,6 +60,13 @@
 std::string create_data_user_ce_package_path_as_user_link(
         const char* volume_uuid, userid_t userid, const char* package_name);
 
+std::string create_data_misc_supplemental_path(const char* volume_uuid, bool isCeData,
+                                               userid_t userid);
+std::string create_data_misc_supplemental_package_path(const char* volume_uuid, bool isCeData,
+                                                       userid_t userid, const char* package_name);
+std::string create_data_misc_supplemental_shared_path(const char* volume_uuid, bool isCeData,
+                                                      userid_t userid, const char* package_name);
+
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user);
 std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user);
 std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user,
@@ -120,6 +127,11 @@
 int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false);
 int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false);
 
+bool is_renamed_deleted_dir(const std::string& path);
+int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = true);
+
+void cleanup_invalid_package_dirs_under_path(const std::string& pathname);
+
 int delete_dir_contents(const char *pathname,
                         int also_delete_dir,
                         int (*exclusion_predicate)(const char *name, const int is_dir),
diff --git a/data/etc/android.hardware.telephony.euicc.mep.xml b/data/etc/android.hardware.telephony.euicc.mep.xml
new file mode 100644
index 0000000..f958e4b
--- /dev/null
+++ b/data/etc/android.hardware.telephony.euicc.mep.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Feature for devices with an eUICC supporting MEP(Multiple enabled profiles) -->
+<permissions>
+    <feature name="android.hardware.telephony.euicc" />
+    <feature name="android.hardware.telephony.euicc.mep" />
+</permissions>
\ No newline at end of file
diff --git a/data/etc/android.software.window_magnification.xml b/data/etc/android.software.window_magnification.xml
new file mode 100644
index 0000000..10a2de1
--- /dev/null
+++ b/data/etc/android.software.window_magnification.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<!-- This is the feature indicating device supports window magnifier.
+     And the hand-held and PC platform should support this.
+-->
+<permissions>
+    <feature name="android.software.window_magnification" />
+</permissions>
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index d601931..8df7fdb 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -46,6 +46,7 @@
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
+    <feature name="android.software.window_magnification" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 68b8def..8fdd8d0 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -54,6 +54,7 @@
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
+    <feature name="android.software.window_magnification" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/pc_core_hardware.xml b/data/etc/pc_core_hardware.xml
index b490ba0..3155710 100644
--- a/data/etc/pc_core_hardware.xml
+++ b/data/etc/pc_core_hardware.xml
@@ -49,6 +49,7 @@
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
+    <feature name="android.software.window_magnification" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
@@ -62,4 +63,4 @@
     <!-- Feature to specify if the device supports freeform. -->
     <feature name="android.software.freeform_window_management" />
     <feature name="android.hardware.type.pc" />
-</permissions>
\ No newline at end of file
+</permissions>
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 8c369de..59d5b10 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -54,6 +54,7 @@
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
+    <feature name="android.software.window_magnification" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 98f0eec..63aa7ff 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -76,7 +76,7 @@
  * It's passed the frame data that should not outlive the callback, as well as the data pointer
  * provided by the application that registered a callback.
  */
-typedef void (*AChoreographer_extendedFrameCallback)(
+typedef void (*AChoreographer_vsyncCallback)(
         const AChoreographerFrameCallbackData* callbackData, void* data);
 
 /**
@@ -134,8 +134,8 @@
  * Posts a callback to run on the next frame. The data pointer provided will
  * be passed to the callback function when it's called.
  */
-void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
-                                        AChoreographer_extendedFrameCallback callback, void* data)
+void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
+                                        AChoreographer_vsyncCallback callback, void* data)
         __INTRODUCED_IN(33);
 
 /**
@@ -215,7 +215,7 @@
 /**
  * The time in nanoseconds which the frame at given index is expected to be presented.
  */
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
 
 /**
diff --git a/include/android/input.h b/include/android/input.h
index e6ad943f..fb5e204 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -808,6 +808,33 @@
 };
 
 /**
+ * Constants that identify different gesture classification types.
+ */
+enum {
+    /**
+     * Classification constant: None.
+     *
+     * No additional information is available about the current motion event stream.
+     */
+    AMOTION_EVENT_CLASSIFICATION_NONE = 0,
+    /**
+     * Classification constant: Ambiguous gesture.
+     *
+     * The user's intent with respect to the current event stream is not yet determined.
+     * Gestural actions, such as scrolling, should be inhibited until the classification resolves
+     * to another value or the event stream ends.
+     */
+    AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE = 1,
+    /**
+     * Classification constant: Deep press.
+     *
+     * The current event stream represents the user intentionally pressing harder on the screen.
+     * This classification type should be used to accelerate the long press behaviour.
+     */
+    AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS = 2,
+};
+
+/**
  * Input source masks.
  *
  * Refer to the documentation on android.view.InputDevice for more details about input sources
@@ -1327,6 +1354,23 @@
         int32_t axis, size_t pointer_index, size_t history_index);
 
 /**
+ * Get the action button for the motion event. Returns a valid action button when the
+ * event is associated with a button press or button release action. For other actions
+ * the return value is undefined.
+ */
+int32_t AMotionEvent_getActionButton(const AInputEvent* motion_event);
+
+/**
+ * Returns the classification for the current gesture.
+ * The classification may change as more events become available for the same gesture.
+ *
+ * @see #AMOTION_EVENT_CLASSIFICATION_NONE
+ * @see #AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE
+ * @see #AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS
+*/
+int32_t AMotionEvent_getClassification(const AInputEvent* motion_event);
+
+/**
  * Creates a native AInputEvent* object that is a copy of the specified Java
  * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific
  * AInputEvent_* functions. The object returned by this function must be disposed using
diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h
index 932af2d..708eaf5 100644
--- a/include/ftl/Flags.h
+++ b/include/ftl/Flags.h
@@ -120,10 +120,10 @@
     }
 
     /* Tests whether any of the given flags are set */
-    bool any(Flags<F> f) { return (mFlags & f.mFlags) != 0; }
+    bool any(Flags<F> f) const { return (mFlags & f.mFlags) != 0; }
 
     /* Tests whether all of the given flags are set */
-    bool all(Flags<F> f) { return (mFlags & f.mFlags) == f.mFlags; }
+    bool all(Flags<F> f) const { return (mFlags & f.mFlags) == f.mFlags; }
 
     Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); }
     Flags<F>& operator|=(Flags<F> rhs) {
@@ -153,6 +153,10 @@
         return *this;
     }
 
+    inline Flags<F>& clear(Flags<F> f = static_cast<F>(~static_cast<U>(0))) {
+        return *this &= ~f;
+    }
+
     Iterator begin() const { return Iterator(*this); }
 
     Iterator end() const { return Iterator(); }
diff --git a/include/ftl/details/array_traits.h b/include/ftl/details/array_traits.h
index 16e63ec..5234c38 100644
--- a/include/ftl/details/array_traits.h
+++ b/include/ftl/details/array_traits.h
@@ -42,7 +42,7 @@
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
   template <typename... Args>
-  static pointer construct_at(const_iterator it, Args&&... args) {
+  static constexpr pointer construct_at(const_iterator it, Args&&... args) {
     void* const ptr = const_cast<void*>(static_cast<const void*>(it));
     if constexpr (std::is_constructible_v<value_type, Args...>) {
       // TODO: Replace with std::construct_at in C++20.
@@ -52,6 +52,42 @@
       return new (ptr) value_type{std::forward<Args>(args)...};
     }
   }
+
+  // TODO: Make constexpr in C++20.
+  template <typename... Args>
+  static reference replace_at(const_iterator it, Args&&... args) {
+    value_type element{std::forward<Args>(args)...};
+    return replace_at(it, std::move(element));
+  }
+
+  // TODO: Make constexpr in C++20.
+  static reference replace_at(const_iterator it, value_type&& value) {
+    std::destroy_at(it);
+    // This is only safe because exceptions are disabled.
+    return *construct_at(it, std::move(value));
+  }
+
+  // TODO: Make constexpr in C++20.
+  static void in_place_swap(reference a, reference b) {
+    value_type c{std::move(a)};
+    replace_at(&a, std::move(b));
+    replace_at(&b, std::move(c));
+  }
+
+  // TODO: Make constexpr in C++20.
+  static void in_place_swap_ranges(iterator first1, iterator last1, iterator first2) {
+    while (first1 != last1) {
+      in_place_swap(*first1++, *first2++);
+    }
+  }
+
+  // TODO: Replace with std::uninitialized_copy in C++20.
+  template <typename Iterator>
+  static void uninitialized_copy(Iterator first, Iterator last, const_iterator out) {
+    while (first != last) {
+      construct_at(out++, *first++);
+    }
+  }
 };
 
 // CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
@@ -101,33 +137,33 @@
 // TODO: Replace with operator<=> in C++20.
 template <template <typename, std::size_t> class Array>
 struct ArrayComparators {
-  template <typename T, std::size_t N, std::size_t M>
-  friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+  template <typename T, typename U, std::size_t N, std::size_t M>
+  friend bool operator==(const Array<T, N>& lhs, const Array<U, M>& rhs) {
     return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
   }
 
-  template <typename T, std::size_t N, std::size_t M>
-  friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+  template <typename T, typename U, std::size_t N, std::size_t M>
+  friend bool operator<(const Array<T, N>& lhs, const Array<U, M>& rhs) {
     return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
   }
 
-  template <typename T, std::size_t N, std::size_t M>
-  friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+  template <typename T, typename U, std::size_t N, std::size_t M>
+  friend bool operator>(const Array<T, N>& lhs, const Array<U, M>& rhs) {
     return rhs < lhs;
   }
 
-  template <typename T, std::size_t N, std::size_t M>
-  friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+  template <typename T, typename U, std::size_t N, std::size_t M>
+  friend bool operator!=(const Array<T, N>& lhs, const Array<U, M>& rhs) {
     return !(lhs == rhs);
   }
 
-  template <typename T, std::size_t N, std::size_t M>
-  friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+  template <typename T, typename U, std::size_t N, std::size_t M>
+  friend bool operator>=(const Array<T, N>& lhs, const Array<U, M>& rhs) {
     return !(lhs < rhs);
   }
 
-  template <typename T, std::size_t N, std::size_t M>
-  friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+  template <typename T, typename U, std::size_t N, std::size_t M>
+  friend bool operator<=(const Array<T, N>& lhs, const Array<U, M>& rhs) {
     return !(lhs > rhs);
   }
 };
diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h
index 2effaa4..5217e76 100644
--- a/include/ftl/small_map.h
+++ b/include/ftl/small_map.h
@@ -65,6 +65,9 @@
 class SmallMap final {
   using Map = SmallVector<std::pair<const K, V>, N>;
 
+  template <typename, typename, std::size_t, typename>
+  friend class SmallMap;
+
  public:
   using key_type = K;
   using mapped_type = V;
@@ -100,6 +103,10 @@
     deduplicate();
   }
 
+  // Copies or moves key-value pairs from a convertible map.
+  template <typename Q, typename W, std::size_t M, typename E>
+  SmallMap(SmallMap<Q, W, M, E> other) : map_(std::move(other.map_)) {}
+
   size_type max_size() const { return map_.max_size(); }
   size_type size() const { return map_.size(); }
   bool empty() const { return map_.empty(); }
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
index 03587e3..339726e 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -37,6 +37,9 @@
 // augmented by an unstable_erase operation that does not preserve order, and a replace operation
 // that destructively emplaces.
 //
+// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data
+// members, or be const itself.
+//
 // SmallVector<T, 0> is a specialization that thinly wraps std::vector.
 //
 // Example usage:
@@ -105,10 +108,9 @@
   SmallVector(Arg&& arg, Args&&... args)
       : vector_(std::in_place_type<Static>, std::forward<Arg>(arg), std::forward<Args>(args)...) {}
 
-  // Copies at most N elements from a smaller convertible vector.
-  template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
-  SmallVector(const SmallVector<U, M>& other)
-      : SmallVector(kIteratorRange, other.begin(), other.end()) {}
+  // Copies or moves elements from a smaller convertible vector.
+  template <typename U, std::size_t M, typename = std::enable_if_t<(M > 0)>>
+  SmallVector(SmallVector<U, M> other) : vector_(convert(std::move(other))) {}
 
   void swap(SmallVector& other) { vector_.swap(other.vector_); }
 
@@ -235,7 +237,30 @@
     }
   }
 
+  // Extracts the elements as std::vector.
+  std::vector<T> promote() && {
+    if (dynamic()) {
+      return std::get<Dynamic>(std::move(vector_)).promote();
+    } else {
+      return {std::make_move_iterator(begin()), std::make_move_iterator(end())};
+    }
+  }
+
  private:
+  template <typename, std::size_t>
+  friend class SmallVector;
+
+  template <typename U, std::size_t M>
+  static std::variant<Static, Dynamic> convert(SmallVector<U, M>&& other) {
+    using Other = SmallVector<U, M>;
+
+    if (other.dynamic()) {
+      return std::get<typename Other::Dynamic>(std::move(other.vector_));
+    } else {
+      return std::get<typename Other::Static>(std::move(other.vector_));
+    }
+  }
+
   template <auto InsertStatic, auto InsertDynamic, typename... Args>
   auto insert(Args&&... args) {
     if (Dynamic* const vector = std::get_if<Dynamic>(&vector_)) {
@@ -267,9 +292,10 @@
 // Partial specialization without static storage.
 template <typename T>
 class SmallVector<T, 0> final : details::ArrayTraits<T>,
+                                details::ArrayComparators<SmallVector>,
                                 details::ArrayIterators<SmallVector<T, 0>, T>,
                                 std::vector<T> {
-  using details::ArrayTraits<T>::construct_at;
+  using details::ArrayTraits<T>::replace_at;
 
   using Iter = details::ArrayIterators<SmallVector, T>;
   using Impl = std::vector<T>;
@@ -291,8 +317,30 @@
   FTL_ARRAY_TRAIT(T, const_iterator);
   FTL_ARRAY_TRAIT(T, const_reverse_iterator);
 
+  // See std::vector for underlying constructors.
   using Impl::Impl;
 
+  // Copies and moves a vector, respectively.
+  SmallVector(const SmallVector&) = default;
+  SmallVector(SmallVector&&) = default;
+
+  // Constructs elements in place. See StaticVector for underlying constructor.
+  template <typename U, std::size_t... Sizes, typename... Types>
+  SmallVector(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list)
+      : SmallVector(SmallVector<T, sizeof...(Sizes)>(std::move(list))) {}
+
+  // Copies or moves elements from a convertible vector.
+  template <typename U, std::size_t M>
+  SmallVector(SmallVector<U, M> other) : Impl(convert(std::move(other))) {}
+
+  SmallVector& operator=(SmallVector other) {
+    // Define copy/move assignment in terms of copy/move construction.
+    swap(other);
+    return *this;
+  }
+
+  void swap(SmallVector& other) { Impl::swap(other); }
+
   using Impl::empty;
   using Impl::max_size;
   using Impl::size;
@@ -324,10 +372,7 @@
 
   template <typename... Args>
   reference replace(const_iterator it, Args&&... args) {
-    value_type element{std::forward<Args>(args)...};
-    std::destroy_at(it);
-    // This is only safe because exceptions are disabled.
-    return *construct_at(it, std::move(element));
+    return replace_at(it, std::forward<Args>(args)...);
   }
 
   template <typename... Args>
@@ -353,7 +398,26 @@
     pop_back();
   }
 
-  void swap(SmallVector& other) { Impl::swap(other); }
+  std::vector<T> promote() && { return std::move(*this); }
+
+ private:
+  template <typename U, std::size_t M>
+  static Impl convert(SmallVector<U, M>&& other) {
+    if constexpr (std::is_constructible_v<Impl, std::vector<U>&&>) {
+      return std::move(other).promote();
+    } else {
+      SmallVector vector(other.size());
+
+      // Consistently with StaticVector, T only requires copy/move construction from U, rather than
+      // copy/move assignment.
+      auto it = vector.begin();
+      for (auto& element : other) {
+        vector.replace(it++, std::move(element));
+      }
+
+      return vector;
+    }
+  }
 };
 
 template <typename>
diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h
index b7f8c29..eb83b85 100644
--- a/include/ftl/static_vector.h
+++ b/include/ftl/static_vector.h
@@ -39,6 +39,9 @@
 // adheres to standard containers, except the unstable_erase operation that does not preserve order,
 // and the replace operation that destructively emplaces.
 //
+// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data
+// members, or be const itself.
+//
 // StaticVector<T, 1> is analogous to an iterable std::optional.
 // StaticVector<T, 0> is an error.
 //
@@ -78,7 +81,14 @@
                            details::ArrayComparators<StaticVector> {
   static_assert(N > 0);
 
+  // For constructor that moves from a smaller convertible vector.
+  template <typename, std::size_t>
+  friend class StaticVector;
+
   using details::ArrayTraits<T>::construct_at;
+  using details::ArrayTraits<T>::replace_at;
+  using details::ArrayTraits<T>::in_place_swap_ranges;
+  using details::ArrayTraits<T>::uninitialized_copy;
 
   using Iter = details::ArrayIterators<StaticVector, T>;
   friend Iter;
@@ -117,14 +127,18 @@
   StaticVector(StaticVector&& other) { swap<true>(other); }
 
   // Copies at most N elements from a smaller convertible vector.
-  template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
+  template <typename U, std::size_t M>
   StaticVector(const StaticVector<U, M>& other)
-      : StaticVector(kIteratorRange, other.begin(), other.end()) {}
+      : StaticVector(kIteratorRange, other.begin(), other.end()) {
+    static_assert(N >= M, "Insufficient capacity");
+  }
 
-  // Copies at most N elements from an array.
+  // Copies at most N elements from a smaller convertible array.
   template <typename U, std::size_t M>
   explicit StaticVector(U (&array)[M])
-      : StaticVector(kIteratorRange, std::begin(array), std::end(array)) {}
+      : StaticVector(kIteratorRange, std::begin(array), std::end(array)) {
+    static_assert(N >= M, "Insufficient capacity");
+  }
 
   // Copies at most N elements from the range [first, last).
   //
@@ -139,7 +153,18 @@
   template <typename Iterator>
   StaticVector(IteratorRangeTag, Iterator first, Iterator last)
       : size_(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
-    std::uninitialized_copy(first, first + size_, begin());
+    uninitialized_copy(first, first + size_, begin());
+  }
+
+  // Moves at most N elements from a smaller convertible vector.
+  template <typename U, std::size_t M>
+  StaticVector(StaticVector<U, M>&& other) {
+    static_assert(N >= M, "Insufficient capacity");
+
+    // Same logic as swap<true>, though M need not be equal to N.
+    std::uninitialized_move(other.begin(), other.end(), begin());
+    std::destroy(other.begin(), other.end());
+    std::swap(size_, other.size_);
   }
 
   // Constructs at most N elements. The template arguments T and N are inferred using the
@@ -178,7 +203,9 @@
   template <typename U, std::size_t Size, std::size_t... Sizes, typename... Types>
   StaticVector(InitializerList<U, std::index_sequence<Size, Sizes...>, Types...>&& list)
       : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence<Size>{},
-                     std::index_sequence<Sizes...>{}, list.tuple) {}
+                     std::index_sequence<Sizes...>{}, list.tuple) {
+    static_assert(sizeof...(Sizes) < N, "Too many elements");
+  }
 
   ~StaticVector() { std::destroy(begin(), end()); }
 
@@ -238,10 +265,7 @@
   //
   template <typename... Args>
   reference replace(const_iterator it, Args&&... args) {
-    value_type element{std::forward<Args>(args)...};
-    std::destroy_at(it);
-    // This is only safe because exceptions are disabled.
-    return *construct_at(it, std::move(element));
+    return replace_at(it, std::forward<Args>(args)...);
   }
 
   // Appends an element, and returns an iterator to it. If the vector is full, the element is not
@@ -380,7 +404,7 @@
     }
 
     // Swap elements [0, min).
-    std::swap_ranges(begin(), begin() + min, other.begin());
+    in_place_swap_ranges(begin(), begin() + min, other.begin());
 
     // No elements to move if sizes are equal.
     if (min == max) return;
diff --git a/include/input/Input.h b/include/input/Input.h
index e421dee..2837add 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -275,23 +275,21 @@
 
 /**
  * Classifications of the current gesture, if available.
- *
- * The following values must be kept in sync with MotionEvent.java
  */
 enum class MotionClassification : uint8_t {
     /**
      * No classification is available.
      */
-    NONE = 0,
+    NONE = AMOTION_EVENT_CLASSIFICATION_NONE,
     /**
      * Too early to classify the current gesture. Need more events. Look for changes in the
      * upcoming motion events.
      */
-    AMBIGUOUS_GESTURE = 1,
+    AMBIGUOUS_GESTURE = AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE,
     /**
      * The current gesture likely represents a user intentionally exerting force on the touchscreen.
      */
-    DEEP_PRESS = 2,
+    DEEP_PRESS = AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS,
 };
 
 /**
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index 406fee0..1504715 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -27,13 +27,6 @@
 
 #include "ParcelValTypes.h"
 
-using android::BAD_TYPE;
-using android::BAD_VALUE;
-using android::NO_ERROR;
-using android::Parcel;
-using android::status_t;
-using android::UNEXPECTED_NULL;
-
 using android::binder::VAL_BOOLEAN;
 using android::binder::VAL_INTEGER;
 using android::binder::VAL_LONG;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 269b086..1821729 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -368,7 +368,7 @@
     int32_t s = android_atomic_add(1, &mThreadPoolSeq);
     pid_t pid = getpid();
     String8 name;
-    name.appendFormat("Binder:%d_%X", pid, s);
+    name.appendFormat("%d_%X:%s", pid, s, mDriverName.c_str());
     return name;
 }
 
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 1446802..38bde3a 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -26,13 +26,9 @@
     static_libs: [
         "libbase",
         "libbinder_random_parcel",
-        "libcgrouprc",
-        "libcgrouprc_format",
         "libcutils",
         "libhidlbase",
         "liblog",
-        "libprocessgroup",
-        "libjsoncpp",
         "libutils",
     ],
 
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
index ee650e5..1740a2b 100644
--- a/libs/ftl/small_map_test.cpp
+++ b/libs/ftl/small_map_test.cpp
@@ -96,6 +96,33 @@
   }
 }
 
+TEST(SmallMap, Assign) {
+  {
+    // Same types; smaller capacity.
+    SmallMap map1 = ftl::init::map<char, std::string>('k', "kilo")('M', "mega")('G', "giga");
+    const SmallMap map2 = ftl::init::map('T', "tera"s)('P', "peta"s);
+
+    map1 = map2;
+    EXPECT_EQ(map1, map2);
+  }
+  {
+    // 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");
+
+    map1 = map2;
+    EXPECT_EQ(map1, map2);
+  }
+  {
+    // Convertible types; zero capacity.
+    SmallMap<char, std::string, 0> map1 = ftl::init::map('M', "mega")('G', "giga");
+    const SmallMap<char, std::string, 0> map2 = ftl::init::map('T', "tera")('P', "peta");
+
+    map1 = map2;
+    EXPECT_EQ(map1, map2);
+  }
+}
+
 TEST(SmallMap, UniqueKeys) {
   {
     // Duplicate mappings are discarded.
diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp
index 4237496..b662a81 100644
--- a/libs/ftl/small_vector_test.cpp
+++ b/libs/ftl/small_vector_test.cpp
@@ -138,6 +138,116 @@
   }
 }
 
+TEST(SmallVector, Copy) {
+  {
+    // Same capacity.
+    const SmallVector vector = {"snow"s, "cone"s};
+
+    SmallVector<const std::string, 2> copy(vector);
+    EXPECT_EQ(copy, vector);
+
+    // The vector is assignable even if T is const.
+    const SmallVector<std::string, 2> other = {"tiramisu"s};
+    copy = other;
+    EXPECT_EQ(copy, other);
+  }
+  {
+    // From smaller capacity.
+    const SmallVector vector = {"snow"s, "cone"s};
+
+    SmallVector<const std::string, 3> copy(vector);
+    EXPECT_EQ(copy, vector);
+
+    // The vector is assignable even if T is const.
+    const SmallVector other = {"tiramisu"s};
+    copy = other;
+    EXPECT_EQ(copy, other);
+  }
+  {
+    // To zero capacity.
+    const SmallVector vector = {"snow"s, "cone"s};
+
+    SmallVector<const std::string, 0> copy(vector);
+    EXPECT_EQ(copy, vector);
+
+    // The vector is assignable even if T is const.
+    const SmallVector other = {"tiramisu"s};
+    copy = other;
+    EXPECT_EQ(copy, other);
+  }
+  {
+    // From/to zero capacity.
+    const SmallVector<std::string, 0> vector = {"snow"s, "cone"s};
+
+    SmallVector<const std::string, 0> copy(vector);
+    EXPECT_EQ(copy, vector);
+
+    // The vector is assignable even if T is const.
+    const SmallVector<std::string, 0> other = {"tiramisu"s};
+    copy = other;
+    EXPECT_EQ(copy, other);
+  }
+}
+
+TEST(SmallVector, Move) {
+  {
+    // Same capacity.
+    SmallVector vector = {"snow"s, "cone"s};
+
+    SmallVector<const std::string, 2> move(std::move(vector));
+    EXPECT_TRUE(vector.empty());
+    EXPECT_EQ(move, (SmallVector{"snow"s, "cone"s}));
+
+    // The vector is assignable even if T is const.
+    SmallVector<std::string, 2> other = {"tiramisu"s};
+    move = std::move(other);
+    EXPECT_TRUE(other.empty());
+    EXPECT_EQ(move, (SmallVector{"tiramisu"s}));
+  }
+  {
+    // From smaller capacity.
+    SmallVector vector = {"snow"s, "cone"s};
+
+    SmallVector<const std::string, 3> move(std::move(vector));
+    EXPECT_TRUE(vector.empty());
+    EXPECT_EQ(move, (SmallVector{"snow"s, "cone"s}));
+
+    // The vector is assignable even if T is const.
+    SmallVector other = {"tiramisu"s};
+    move = std::move(other);
+    EXPECT_TRUE(other.empty());
+    EXPECT_EQ(move, (SmallVector{"tiramisu"s}));
+  }
+  {
+    // To zero capacity.
+    SmallVector vector = {"snow"s, "cone"s};
+
+    SmallVector<const std::string, 0> move(std::move(vector));
+    EXPECT_TRUE(vector.empty());
+    EXPECT_EQ(move, (SmallVector{"snow"s, "cone"s}));
+
+    // The vector is assignable even if T is const.
+    SmallVector other = {"tiramisu"s};
+    move = std::move(other);
+    EXPECT_TRUE(other.empty());
+    EXPECT_EQ(move, (SmallVector{"tiramisu"s}));
+  }
+  {
+    // From/to zero capacity.
+    SmallVector<std::string, 0> vector = {"snow"s, "cone"s};
+
+    SmallVector<const std::string, 0> move(std::move(vector));
+    EXPECT_TRUE(vector.empty());
+    EXPECT_EQ(move, (SmallVector{"snow"s, "cone"s}));
+
+    // The vector is assignable even if T is const.
+    SmallVector<std::string, 0> other = {"tiramisu"s};
+    move = std::move(other);
+    EXPECT_TRUE(other.empty());
+    EXPECT_EQ(move, (SmallVector{"tiramisu"s}));
+  }
+}
+
 TEST(SmallVector, String) {
   SmallVector<char, 10> chars;
   char c = 'a';
@@ -366,21 +476,20 @@
   bool alive = true;
 };
 
-void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
-  std::swap(lhs.alive, rhs.alive);
-}
-
 }  // namespace
 
 TEST(SmallVector, Destroy) {
   int live = 0;
   int dead = 0;
-
-  { SmallVector<DestroyCounts, 3> counts; }
+  {
+    // Empty.
+    SmallVector<DestroyCounts, 3> counts;
+  }
   EXPECT_EQ(0, live);
   EXPECT_EQ(0, dead);
 
   {
+    // Static.
     SmallVector<DestroyCounts, 3> counts;
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
@@ -393,6 +502,7 @@
 
   live = 0;
   {
+    // Dynamic.
     SmallVector<DestroyCounts, 3> counts;
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
@@ -406,12 +516,13 @@
 
   live = dead = 0;
   {
+    // Copy.
     SmallVector<DestroyCounts, 2> counts;
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
 
-    auto copy = counts;
+    const auto copy = counts;
     EXPECT_TRUE(copy.dynamic());
   }
   EXPECT_EQ(6, live);
@@ -419,12 +530,13 @@
 
   live = dead = 0;
   {
+    // Move.
     SmallVector<DestroyCounts, 2> counts;
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
 
-    auto move = std::move(counts);
+    const auto move = std::move(counts);
     EXPECT_TRUE(move.dynamic());
   }
   EXPECT_EQ(3, live);
@@ -432,6 +544,7 @@
 
   live = dead = 0;
   {
+    // Swap.
     SmallVector<DestroyCounts, 2> counts1;
     counts1.emplace_back(live, dead);
     counts1.emplace_back(live, dead);
@@ -448,7 +561,10 @@
 
     swap(counts1, counts2);
 
+    EXPECT_EQ(1u, counts1.size());
     EXPECT_FALSE(counts1.dynamic());
+
+    EXPECT_EQ(3u, counts2.size());
     EXPECT_TRUE(counts2.dynamic());
 
     EXPECT_EQ(0, live);
@@ -465,29 +581,82 @@
   int dead = 0;
 
   SmallVector<DestroyCounts, 2> counts;
-  counts.emplace_back(live, dead);
-  counts.emplace_back(live, dead);
+  {
+    // Static.
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
 
-  counts.clear();
+    counts.clear();
 
-  EXPECT_TRUE(counts.empty());
-  EXPECT_FALSE(counts.dynamic());
-
+    EXPECT_TRUE(counts.empty());
+    EXPECT_FALSE(counts.dynamic());
+  }
   EXPECT_EQ(2, live);
   EXPECT_EQ(0, dead);
 
   live = 0;
-  counts.emplace_back(live, dead);
-  counts.emplace_back(live, dead);
-  counts.emplace_back(live, dead);
+  {
+    // Dynamic.
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
 
-  counts.clear();
+    counts.clear();
 
-  EXPECT_TRUE(counts.empty());
-  EXPECT_TRUE(counts.dynamic());
-
+    EXPECT_TRUE(counts.empty());
+    EXPECT_TRUE(counts.dynamic());
+  }
   EXPECT_EQ(3, live);
   EXPECT_EQ(2, dead);
 }
 
+TEST(SmallVector, Promote) {
+  {
+    const std::vector vector = {"snow"s, "cone"s};
+    EXPECT_EQ(vector, SmallVector("snow"s, "cone"s).promote());
+    EXPECT_EQ(vector, (SmallVector<std::string, 0>({"snow"s, "cone"s}).promote()));
+  }
+
+  int live = 0;
+  int dead = 0;
+
+  std::vector<DestroyCounts> vector;
+  {
+    // Static.
+    SmallVector<DestroyCounts, 3> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+
+    vector = std::move(counts).promote();
+
+    ASSERT_EQ(2u, vector.size());
+    EXPECT_TRUE(vector[0].alive);
+    EXPECT_TRUE(vector[1].alive);
+  }
+  EXPECT_EQ(0, live);
+  EXPECT_EQ(2, dead);
+
+  vector.clear();
+  live = dead = 0;
+  {
+    // Dynamic.
+    SmallVector<DestroyCounts, 2> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+
+    EXPECT_EQ(2, dead);
+    dead = 0;
+
+    vector = std::move(counts).promote();
+
+    ASSERT_EQ(3u, vector.size());
+    EXPECT_TRUE(vector[0].alive);
+    EXPECT_TRUE(vector[1].alive);
+    EXPECT_TRUE(vector[2].alive);
+  }
+  EXPECT_EQ(0, live);
+  EXPECT_EQ(0, dead);
+}
+
 }  // namespace android::test
diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp
index 2de3ad2..0e10a5d 100644
--- a/libs/ftl/static_vector_test.cpp
+++ b/libs/ftl/static_vector_test.cpp
@@ -144,6 +144,64 @@
   }
 }
 
+TEST(StaticVector, Copy) {
+  {
+    // Same capacity.
+    const StaticVector vector = {"snow"s, "cone"s};
+
+    StaticVector<const std::string, 2> copy(vector);
+    EXPECT_EQ(copy, vector);
+
+    // The vector is assignable even if T is const.
+    const StaticVector<std::string, 2> other = {"tiramisu"s};
+    copy = other;
+    EXPECT_EQ(copy, other);
+  }
+  {
+    // From smaller capacity.
+    const StaticVector vector = {"snow"s, "cone"s};
+
+    StaticVector<const std::string, 3> copy(vector);
+    EXPECT_EQ(copy, vector);
+
+    // The vector is assignable even if T is const.
+    const StaticVector other = {"tiramisu"s};
+    copy = other;
+    EXPECT_EQ(copy, other);
+  }
+}
+
+TEST(StaticVector, Move) {
+  {
+    // Same capacity.
+    StaticVector vector = {"snow"s, "cone"s};
+
+    StaticVector<const std::string, 2> move(std::move(vector));
+    EXPECT_TRUE(vector.empty());
+    EXPECT_EQ(move, (StaticVector{"snow"s, "cone"s}));
+
+    // The vector is assignable even if T is const.
+    StaticVector<std::string, 2> other = {"tiramisu"s};
+    move = std::move(other);
+    EXPECT_TRUE(other.empty());
+    EXPECT_EQ(move, (StaticVector{"tiramisu"s}));
+  }
+  {
+    // From smaller capacity.
+    StaticVector vector = {"snow"s, "cone"s};
+
+    StaticVector<const std::string, 3> move(std::move(vector));
+    EXPECT_TRUE(vector.empty());
+    EXPECT_EQ(move, (StaticVector{"snow"s, "cone"s}));
+
+    // The vector is assignable even if T is const.
+    StaticVector other = {"tiramisu"s};
+    move = std::move(other);
+    EXPECT_TRUE(other.empty());
+    EXPECT_EQ(move, (StaticVector{"tiramisu"s}));
+  }
+}
+
 TEST(StaticVector, String) {
   StaticVector<char, 10> chars;
   char c = 'a';
@@ -328,21 +386,20 @@
   bool alive = true;
 };
 
-void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
-  std::swap(lhs.alive, rhs.alive);
-}
-
 }  // namespace
 
 TEST(StaticVector, Destroy) {
   int live = 0;
   int dead = 0;
-
-  { StaticVector<DestroyCounts, 5> counts; }
+  {
+    // Empty.
+    StaticVector<DestroyCounts, 5> counts;
+  }
   EXPECT_EQ(0, live);
   EXPECT_EQ(0, dead);
 
   {
+    // Non-empty.
     StaticVector<DestroyCounts, 5> counts;
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
@@ -353,30 +410,33 @@
 
   live = 0;
   {
+    // Copy.
     StaticVector<DestroyCounts, 5> counts;
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
 
-    auto copy = counts;
+    const auto copy = counts;
   }
   EXPECT_EQ(6, live);
   EXPECT_EQ(0, dead);
 
   live = 0;
   {
+    // Move.
     StaticVector<DestroyCounts, 5> counts;
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
     counts.emplace_back(live, dead);
 
-    auto move = std::move(counts);
+    const auto move = std::move(counts);
   }
   EXPECT_EQ(3, live);
   EXPECT_EQ(3, dead);
 
   live = dead = 0;
   {
+    // Swap.
     StaticVector<DestroyCounts, 5> counts1;
     counts1.emplace_back(live, dead);
     counts1.emplace_back(live, dead);
@@ -384,29 +444,34 @@
 
     StaticVector<DestroyCounts, 5> counts2;
     counts2.emplace_back(live, dead);
+    counts2.emplace_back(live, dead);
 
     swap(counts1, counts2);
 
+    EXPECT_EQ(2u, counts1.size());
+    EXPECT_EQ(3u, counts2.size());
+
     EXPECT_EQ(0, live);
-    EXPECT_EQ(2, dead);
+    EXPECT_EQ(7, dead);  // 3 moves per swap, plus 1 move.
 
     dead = 0;
   }
-  EXPECT_EQ(4, live);
+  EXPECT_EQ(5, live);
   EXPECT_EQ(0, dead);
 }
 
 TEST(StaticVector, Clear) {
   int live = 0;
   int dead = 0;
+  {
+    StaticVector<DestroyCounts, 5> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
 
-  StaticVector<DestroyCounts, 5> counts;
-  counts.emplace_back(live, dead);
-  counts.emplace_back(live, dead);
+    counts.clear();
 
-  counts.clear();
-
-  EXPECT_TRUE(counts.empty());
+    EXPECT_TRUE(counts.empty());
+  }
   EXPECT_EQ(2, live);
   EXPECT_EQ(0, dead);
 }
diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS
index 93879d8..72ff978 100644
--- a/libs/gralloc/OWNERS
+++ b/libs/gralloc/OWNERS
@@ -1 +1,4 @@
+# Graphics team
+alecmouri@google.com
 chrisforbes@google.com
+jreck@google.com
\ No newline at end of file
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 61e6657..68b99b0 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -196,6 +196,35 @@
 status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType);
 
 /**
+ * Private helper functions
+ */
+template <class T>
+status_t encodeInteger(const T& input, OutputHidlVec* output) {
+    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
+    return output->encode(tmp, sizeof(input));
+}
+
+template <class T>
+status_t decodeInteger(InputHidlVec* input, T* output) {
+    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
+    return input->decode(tmp, sizeof(*output));
+}
+
+/**
  * encode/encodeMetadata are the main encoding functions. They take in T and uses the encodeHelper
  * function to turn T into the hidl_vec byte stream.
  *
@@ -251,10 +280,45 @@
 template <class T>
 status_t encodeOptionalMetadata(const MetadataType& metadataType, const std::optional<T>& input,
                         hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
-    if (!input) {
-        return NO_ERROR;
+    OutputHidlVec outputHidlVec{output};
+
+    status_t err = encodeMetadataType(metadataType, &outputHidlVec);
+    if (err) {
+        return err;
     }
-    return encodeMetadata(metadataType, *input, output, encodeHelper);
+
+    err = encodeInteger<uint32_t>(input.has_value() ? 1 : 0, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    if (input) {
+        err = encodeHelper(*input, &outputHidlVec);
+        if (err) {
+            return err;
+        }
+    }
+
+    err = outputHidlVec.resize();
+    if (err) {
+        return err;
+    }
+
+    err = encodeMetadataType(metadataType, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    err = encodeInteger<uint32_t>(input.has_value() ? 1 : 0, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    if (input) {
+        return encodeHelper(*input, &outputHidlVec);
+    }
+
+    return NO_ERROR;
 }
 
 /**
@@ -315,45 +379,36 @@
     if (!output) {
         return BAD_VALUE;
     }
-    if (input.size() <= 0) {
-        output->reset();
-        return NO_ERROR;
+
+    InputHidlVec inputHidlVec{&input};
+
+    status_t err = validateMetadataType(&inputHidlVec, metadataType);
+    if (err) {
+        return err;
     }
-    T tmp;
-    status_t err = decodeMetadata(metadataType, input, &tmp, decodeHelper);
-    if (!err) {
+
+    uint32_t present = 0;
+    err = decodeInteger<uint32_t>(&inputHidlVec, &present);
+    if (err) {
+        return err;
+    }
+
+    if (present) {
+        T tmp;
+        err = decodeHelper(&inputHidlVec, &tmp);
+        if (err) {
+            return err;
+        }
+
         *output = tmp;
     }
-    return err;
-}
 
-/**
- * Private helper functions
- */
-template <class T>
-status_t encodeInteger(const T& input, OutputHidlVec* output) {
-    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
-                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
-                  std::is_same<T, float>::value || std::is_same<T, double>::value);
-    if (!output) {
+    err = inputHidlVec.hasRemainingData();
+    if (err) {
         return BAD_VALUE;
     }
 
-    const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
-    return output->encode(tmp, sizeof(input));
-}
-
-template <class T>
-status_t decodeInteger(InputHidlVec* input, T* output) {
-    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
-                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
-                  std::is_same<T, float>::value || std::is_same<T, double>::value);
-    if (!output) {
-        return BAD_VALUE;
-    }
-
-    uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
-    return input->decode(tmp, sizeof(*output));
+    return NO_ERROR;
 }
 
 status_t encodeString(const std::string& input, OutputHidlVec* output) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index e17b2c8..d634c58 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -37,6 +37,12 @@
         "android.hardware.graphics.bufferqueue@2.0",
     ],
     min_sdk_version: "29",
+    // TODO(b/218719284) can media use be constrained to libgui_bufferqueue_static?
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+        "test_com.android.media.swcodec",
+    ],
 }
 
 cc_library_headers {
@@ -339,6 +345,7 @@
         "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/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 26db59b..39d380d 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -126,7 +126,7 @@
         ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
               ", displayId=%s, count=%d, vsyncId=%" PRId64,
               this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
-              vsyncEventData.id);
+              vsyncEventData.preferredVsyncId());
         mWaitingForVsync = false;
         mLastVsyncCount = vsyncCount;
         dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
@@ -146,18 +146,6 @@
     return 1; // keep the callback
 }
 
-void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event,
-                                                    VsyncEventData* outVsyncEventData) const {
-    for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
-        DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline =
-                event.vsync.frameTimelines[i];
-        outVsyncEventData->frameTimelines[i] =
-                VsyncEventData::FrameTimeline(receiverTimeline.vsyncId,
-                                              receiverTimeline.deadlineTimestamp,
-                                              receiverTimeline.expectedVSyncTimestamp);
-    }
-}
-
 bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
                                                   PhysicalDisplayId* outDisplayId,
                                                   uint32_t* outCount,
@@ -178,12 +166,7 @@
                     *outTimestamp = ev.header.timestamp;
                     *outDisplayId = ev.header.displayId;
                     *outCount = ev.vsync.count;
-                    outVsyncEventData->id = ev.vsync.vsyncId;
-                    outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
-                    outVsyncEventData->frameInterval = ev.vsync.frameInterval;
-                    outVsyncEventData->preferredFrameTimelineIndex =
-                            ev.vsync.preferredFrameTimelineIndex;
-                    populateFrameTimelines(ev, outVsyncEventData);
+                    *outVsyncEventData = ev.vsync.vsyncData;
                     break;
                 case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                     dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 36e7d80..bfb7769 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -40,7 +40,13 @@
         mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration);
         if (mEventConnection != nullptr) {
             mDataChannel = std::make_unique<gui::BitTube>();
-            mEventConnection->stealReceiveChannel(mDataChannel.get());
+            const auto status = mEventConnection->stealReceiveChannel(mDataChannel.get());
+            if (!status.isOk()) {
+                ALOGE("stealReceiveChannel failed: %s", status.toString8().c_str());
+                mInitError = std::make_optional<status_t>(status.transactionError());
+                mDataChannel.reset();
+                mEventConnection.clear();
+            }
         }
     }
 }
@@ -51,12 +57,11 @@
 status_t DisplayEventReceiver::initCheck() const {
     if (mDataChannel != nullptr)
         return NO_ERROR;
-    return NO_INIT;
+    return mInitError.has_value() ? mInitError.value() : NO_INIT;
 }
 
 int DisplayEventReceiver::getFd() const {
-    if (mDataChannel == nullptr)
-        return NO_INIT;
+    if (mDataChannel == nullptr) return mInitError.has_value() ? mInitError.value() : NO_INIT;
 
     return mDataChannel->getFd();
 }
@@ -69,7 +74,7 @@
         mEventConnection->setVsyncRate(count);
         return NO_ERROR;
     }
-    return NO_INIT;
+    return mInitError.has_value() ? mInitError.value() : NO_INIT;
 }
 
 status_t DisplayEventReceiver::requestNextVsync() {
@@ -77,18 +82,17 @@
         mEventConnection->requestNextVsync();
         return NO_ERROR;
     }
-    return NO_INIT;
+    return mInitError.has_value() ? mInitError.value() : NO_INIT;
 }
 
-status_t DisplayEventReceiver::getLatestVsyncEventData(VsyncEventData* outVsyncEventData) const {
+status_t DisplayEventReceiver::getLatestVsyncEventData(
+        ParcelableVsyncEventData* outVsyncEventData) const {
     if (mEventConnection != nullptr) {
-        VsyncEventData vsyncEventData;
-        auto status = mEventConnection->getLatestVsyncEventData(&vsyncEventData);
+        auto status = mEventConnection->getLatestVsyncEventData(outVsyncEventData);
         if (!status.isOk()) {
             ALOGE("Failed to get latest vsync event data: %s", status.exceptionMessage().c_str());
             return status.transactionError();
         }
-        *outVsyncEventData = vsyncEventData;
         return NO_ERROR;
     }
     return NO_INIT;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index fb9ed22..75c5e26 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -42,6 +42,8 @@
 
 // ---------------------------------------------------------------------------
 
+using namespace aidl::android::hardware::graphics;
+
 namespace android {
 
 using gui::IDisplayEventConnection;
@@ -1201,8 +1203,9 @@
         return NO_ERROR;
     }
 
-    status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
-                                         bool* outSupport) const override {
+    status_t getDisplayDecorationSupport(
+            const sp<IBinder>& displayToken,
+            std::optional<common::DisplayDecorationSupport>* outSupport) const override {
         Parcel data, reply;
         status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (error != NO_ERROR) {
@@ -1225,7 +1228,26 @@
             ALOGE("getDisplayDecorationSupport: failed to read support: %d", error);
             return error;
         }
-        *outSupport = support;
+
+        if (support) {
+            int32_t format, alphaInterpretation;
+            error = reply.readInt32(&format);
+            if (error != NO_ERROR) {
+                ALOGE("getDisplayDecorationSupport: failed to read format: %d", error);
+                return error;
+            }
+            error = reply.readInt32(&alphaInterpretation);
+            if (error != NO_ERROR) {
+                ALOGE("getDisplayDecorationSupport: failed to read alphaInterpretation: %d", error);
+                return error;
+            }
+            outSupport->emplace();
+            outSupport->value().format = static_cast<common::PixelFormat>(format);
+            outSupport->value().alphaInterpretation =
+                    static_cast<common::AlphaInterpretation>(alphaInterpretation);
+        } else {
+            outSupport->reset();
+        }
         return NO_ERROR;
     }
 
@@ -2164,14 +2186,18 @@
         case GET_DISPLAY_DECORATION_SUPPORT: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken;
-            status_t error = data.readNullableStrongBinder(&displayToken);
+            SAFE_PARCEL(data.readNullableStrongBinder, &displayToken);
+            std::optional<common::DisplayDecorationSupport> support;
+            auto error = getDisplayDecorationSupport(displayToken, &support);
             if (error != NO_ERROR) {
-                ALOGE("getDisplayDecorationSupport: failed to read display token: %d", error);
+                ALOGE("getDisplayDecorationSupport failed with error %d", error);
                 return error;
             }
-            bool support = false;
-            error = getDisplayDecorationSupport(displayToken, &support);
-            reply->writeBool(support);
+            reply->writeBool(support.has_value());
+            if (support) {
+                reply->writeInt32(static_cast<int32_t>(support.value().format));
+                reply->writeInt32(static_cast<int32_t>(support.value().alphaInterpretation));
+            }
             return error;
         }
         case SET_FRAME_RATE: {
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index aa7ebc9..f7392d4 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -111,7 +111,14 @@
 
 status_t SurfaceStats::writeToParcel(Parcel* output) const {
     SAFE_PARCEL(output->writeStrongBinder, surfaceControl);
-    SAFE_PARCEL(output->writeInt64, acquireTime);
+    if (const auto* acquireFence = std::get_if<sp<Fence>>(&acquireTimeOrFence)) {
+        SAFE_PARCEL(output->writeBool, true);
+        SAFE_PARCEL(output->write, **acquireFence);
+    } else {
+        SAFE_PARCEL(output->writeBool, false);
+        SAFE_PARCEL(output->writeInt64, std::get<nsecs_t>(acquireTimeOrFence));
+    }
+
     if (previousReleaseFence) {
         SAFE_PARCEL(output->writeBool, true);
         SAFE_PARCEL(output->write, *previousReleaseFence);
@@ -131,10 +138,20 @@
 
 status_t SurfaceStats::readFromParcel(const Parcel* input) {
     SAFE_PARCEL(input->readStrongBinder, &surfaceControl);
-    SAFE_PARCEL(input->readInt64, &acquireTime);
+
     bool hasFence = false;
     SAFE_PARCEL(input->readBool, &hasFence);
     if (hasFence) {
+        acquireTimeOrFence = sp<Fence>::make();
+        SAFE_PARCEL(input->read, *std::get<sp<Fence>>(acquireTimeOrFence));
+    } else {
+        nsecs_t acquireTime;
+        SAFE_PARCEL(input->readInt64, &acquireTime);
+        acquireTimeOrFence = acquireTime;
+    }
+
+    SAFE_PARCEL(input->readBool, &hasFence);
+    if (hasFence) {
         previousReleaseFence = new Fence();
         SAFE_PARCEL(input->read, *previousReleaseFence);
     }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index eec4a87..9022e7d 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -35,7 +35,9 @@
 using gui::WindowInfoHandle;
 
 layer_state_t::layer_state_t()
-      : what(0),
+      : surface(nullptr),
+        layerId(-1),
+        what(0),
         x(0),
         y(0),
         z(0),
@@ -153,8 +155,12 @@
     SAFE_PARCEL(output.writeBool, isTrustedOverlay);
 
     SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode));
-    SAFE_PARCEL(output.writeNullableParcelable,
-                bufferData ? std::make_optional<BufferData>(*bufferData) : std::nullopt);
+
+    const bool hasBufferData = (bufferData != nullptr);
+    SAFE_PARCEL(output.writeBool, hasBufferData);
+    if (hasBufferData) {
+        SAFE_PARCEL(output.writeParcelable, *bufferData);
+    }
     return NO_ERROR;
 }
 
@@ -264,9 +270,15 @@
     uint32_t mode;
     SAFE_PARCEL(input.readUint32, &mode);
     dropInputMode = static_cast<gui::DropInputMode>(mode);
-    std::optional<BufferData> tmpBufferData;
-    SAFE_PARCEL(input.readParcelable, &tmpBufferData);
-    bufferData = tmpBufferData ? std::make_shared<BufferData>(*tmpBufferData) : nullptr;
+
+    bool hasBufferData;
+    SAFE_PARCEL(input.readBool, &hasBufferData);
+    if (hasBufferData) {
+        bufferData = std::make_shared<BufferData>();
+        SAFE_PARCEL(input.readParcelable, bufferData.get());
+    } else {
+        bufferData = nullptr;
+    }
     return NO_ERROR;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 91b2fb1..9269c3e 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -52,6 +52,7 @@
 
 namespace android {
 
+using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
 using gui::FocusRequest;
 using gui::IRegionSamplingListener;
 using gui::WindowInfo;
@@ -296,7 +297,7 @@
                 surfaceControlStats
                         .emplace_back(callbacksMap[callbackId]
                                               .surfaceControls[surfaceStats.surfaceControl],
-                                      transactionStats.latchTime, surfaceStats.acquireTime,
+                                      transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
                                       transactionStats.presentFence,
                                       surfaceStats.previousReleaseFence, surfaceStats.transformHint,
                                       surfaceStats.eventStats);
@@ -321,7 +322,7 @@
                 surfaceControlStats
                         .emplace_back(callbacksMap[callbackId]
                                               .surfaceControls[surfaceStats.surfaceControl],
-                                      transactionStats.latchTime, surfaceStats.acquireTime,
+                                      transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
                                       transactionStats.presentFence,
                                       surfaceStats.previousReleaseFence, surfaceStats.transformHint,
                                       surfaceStats.eventStats);
@@ -2239,8 +2240,9 @@
                                                                           lightRadius);
 }
 
-bool SurfaceComposerClient::getDisplayDecorationSupport(const sp<IBinder>& displayToken) {
-    bool support = false;
+std::optional<DisplayDecorationSupport> SurfaceComposerClient::getDisplayDecorationSupport(
+        const sp<IBinder>& displayToken) {
+    std::optional<DisplayDecorationSupport> support;
     ComposerService::getComposerService()->getDisplayDecorationSupport(displayToken, &support);
     return support;
 }
diff --git a/libs/gui/VsyncEventData.cpp b/libs/gui/VsyncEventData.cpp
index aad81d0..23f0921 100644
--- a/libs/gui/VsyncEventData.cpp
+++ b/libs/gui/VsyncEventData.cpp
@@ -23,52 +23,46 @@
 
 namespace android::gui {
 
-status_t VsyncEventData::readFromParcel(const Parcel* parcel) {
+int64_t VsyncEventData::preferredVsyncId() const {
+    return frameTimelines[preferredFrameTimelineIndex].vsyncId;
+}
+
+int64_t VsyncEventData::preferredDeadlineTimestamp() const {
+    return frameTimelines[preferredFrameTimelineIndex].deadlineTimestamp;
+}
+
+int64_t VsyncEventData::preferredExpectedPresentationTime() const {
+    return frameTimelines[preferredFrameTimelineIndex].expectedPresentationTime;
+}
+
+status_t ParcelableVsyncEventData::readFromParcel(const Parcel* parcel) {
     if (parcel == nullptr) {
         ALOGE("%s: Null parcel", __func__);
         return BAD_VALUE;
     }
 
-    SAFE_PARCEL(parcel->readInt64, &id)
-    SAFE_PARCEL(parcel->readInt64, &deadlineTimestamp);
-    SAFE_PARCEL(parcel->readInt64, &frameInterval);
+    SAFE_PARCEL(parcel->readInt64, &vsync.frameInterval);
 
     uint64_t uintPreferredFrameTimelineIndex;
     SAFE_PARCEL(parcel->readUint64, &uintPreferredFrameTimelineIndex);
-    preferredFrameTimelineIndex = static_cast<size_t>(uintPreferredFrameTimelineIndex);
+    vsync.preferredFrameTimelineIndex = static_cast<size_t>(uintPreferredFrameTimelineIndex);
 
-    std::vector<FrameTimeline> timelines;
-    SAFE_PARCEL(parcel->readParcelableVector, &timelines);
-    std::copy_n(timelines.begin(), timelines.size(), frameTimelines.begin());
-
-    return OK;
-}
-status_t VsyncEventData::writeToParcel(Parcel* parcel) const {
-    SAFE_PARCEL(parcel->writeInt64, id)
-    SAFE_PARCEL(parcel->writeInt64, deadlineTimestamp);
-    SAFE_PARCEL(parcel->writeInt64, frameInterval);
-    SAFE_PARCEL(parcel->writeUint64, preferredFrameTimelineIndex);
-    SAFE_PARCEL(parcel->writeParcelableVector,
-                std::vector(frameTimelines.begin(), frameTimelines.end()));
-
-    return OK;
-}
-status_t VsyncEventData::FrameTimeline::readFromParcel(const Parcel* parcel) {
-    if (parcel == nullptr) {
-        ALOGE("%s: Null parcel", __func__);
-        return BAD_VALUE;
+    for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+        SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].vsyncId);
+        SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].deadlineTimestamp);
+        SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].expectedPresentationTime);
     }
 
-    SAFE_PARCEL(parcel->readInt64, &id)
-    SAFE_PARCEL(parcel->readInt64, &deadlineTimestamp);
-    SAFE_PARCEL(parcel->readInt64, &expectedPresentTime);
-
     return OK;
 }
-status_t VsyncEventData::FrameTimeline::writeToParcel(Parcel* parcel) const {
-    SAFE_PARCEL(parcel->writeInt64, id);
-    SAFE_PARCEL(parcel->writeInt64, deadlineTimestamp);
-    SAFE_PARCEL(parcel->writeInt64, expectedPresentTime);
+status_t ParcelableVsyncEventData::writeToParcel(Parcel* parcel) const {
+    SAFE_PARCEL(parcel->writeInt64, vsync.frameInterval);
+    SAFE_PARCEL(parcel->writeUint64, vsync.preferredFrameTimelineIndex);
+    for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+        SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].vsyncId);
+        SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].deadlineTimestamp);
+        SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].expectedPresentationTime);
+    }
 
     return OK;
 }
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index a866786..80bd638 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -18,7 +18,6 @@
 #define LOG_TAG "WindowInfo"
 #define LOG_NDEBUG 0
 
-#include <android-base/stringprintf.h>
 #include <binder/Parcel.h>
 #include <gui/WindowInfo.h>
 
@@ -27,6 +26,14 @@
 namespace android::gui {
 
 // --- WindowInfo ---
+void WindowInfo::setInputConfig(Flags<InputConfig> config, bool value) {
+    if (value) {
+        inputConfig |= config;
+        return;
+    }
+    inputConfig &= ~config;
+}
+
 void WindowInfo::addTouchableRegion(const Rect& region) {
     touchableRegion.orSelf(region);
 }
@@ -40,7 +47,7 @@
 }
 
 bool WindowInfo::supportsSplitTouch() const {
-    return flags.test(Flag::SPLIT_TOUCH);
+    return !inputConfig.test(InputConfig::PREVENT_SPLITTING);
 }
 
 bool WindowInfo::isSpy() const {
@@ -58,20 +65,19 @@
 }
 
 bool WindowInfo::operator==(const WindowInfo& info) const {
-    return info.token == token && info.id == id && info.name == name && info.flags == flags &&
-            info.type == type && info.dispatchingTimeout == dispatchingTimeout &&
-            info.frameLeft == frameLeft && info.frameTop == frameTop &&
-            info.frameRight == frameRight && info.frameBottom == frameBottom &&
-            info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
-            info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
-            info.visible == visible && info.trustedOverlay == trustedOverlay &&
-            info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
-            info.hasWallpaper == hasWallpaper && info.paused == paused &&
-            info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
-            info.packageName == packageName && info.inputFeatures == inputFeatures &&
+    return info.token == token && info.id == id && info.name == name &&
+            info.dispatchingTimeout == dispatchingTimeout && info.frameLeft == frameLeft &&
+            info.frameTop == frameTop && info.frameRight == frameRight &&
+            info.frameBottom == frameBottom && info.surfaceInset == surfaceInset &&
+            info.globalScaleFactor == globalScaleFactor && info.transform == transform &&
+            info.touchableRegion.hasSameRects(touchableRegion) &&
+            info.touchOcclusionMode == touchOcclusionMode && info.ownerPid == ownerPid &&
+            info.ownerUid == ownerUid && info.packageName == packageName &&
+            info.inputFeatures == inputFeatures && info.inputConfig == inputConfig &&
             info.displayId == displayId &&
             info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
-            info.applicationInfo == applicationInfo;
+            info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType &&
+            info.layoutParamsFlags == layoutParamsFlags;
 }
 
 status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
@@ -85,13 +91,18 @@
     }
     parcel->writeInt32(1);
 
+    // Ensure that the size of the flags that we use is 32 bits for writing into the parcel.
+    static_assert(sizeof(inputFeatures) == 4u);
+    static_assert(sizeof(inputConfig) == 4u);
+
     // clang-format off
     status_t status = parcel->writeStrongBinder(token) ?:
         parcel->writeInt64(dispatchingTimeout.count()) ?:
         parcel->writeInt32(id) ?:
         parcel->writeUtf8AsUtf16(name) ?:
-        parcel->writeInt32(flags.get()) ?:
-        parcel->writeInt32(static_cast<std::underlying_type_t<WindowInfo::Type>>(type)) ?:
+        parcel->writeInt32(layoutParamsFlags.get()) ?:
+        parcel->writeInt32(
+                static_cast<std::underlying_type_t<WindowInfo::Type>>(layoutParamsType)) ?:
         parcel->writeInt32(frameLeft) ?:
         parcel->writeInt32(frameTop) ?:
         parcel->writeInt32(frameRight) ?:
@@ -105,16 +116,12 @@
         parcel->writeFloat(transform.dtdy()) ?:
         parcel->writeFloat(transform.dsdy()) ?:
         parcel->writeFloat(transform.ty()) ?:
-        parcel->writeBool(visible) ?:
-        parcel->writeBool(focusable) ?:
-        parcel->writeBool(hasWallpaper) ?:
-        parcel->writeBool(paused) ?:
-        parcel->writeBool(trustedOverlay) ?:
         parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
         parcel->writeInt32(ownerPid) ?:
         parcel->writeInt32(ownerUid) ?:
         parcel->writeUtf8AsUtf16(packageName) ?:
         parcel->writeInt32(inputFeatures.get()) ?:
+        parcel->writeInt32(inputConfig.get()) ?:
         parcel->writeInt32(displayId) ?:
         applicationInfo.writeToParcel(parcel) ?:
         parcel->write(touchableRegion) ?:
@@ -141,8 +148,8 @@
         return status;
     }
 
-    flags = Flags<Flag>(parcel->readInt32());
-    type = static_cast<Type>(parcel->readInt32());
+    layoutParamsFlags = Flags<Flag>(parcel->readInt32());
+    layoutParamsType = static_cast<Type>(parcel->readInt32());
     float dsdx, dtdx, tx, dtdy, dsdy, ty;
     int32_t touchOcclusionModeInt;
     // clang-format off
@@ -159,11 +166,6 @@
         parcel->readFloat(&dtdy) ?:
         parcel->readFloat(&dsdy) ?:
         parcel->readFloat(&ty) ?:
-        parcel->readBool(&visible) ?:
-        parcel->readBool(&focusable) ?:
-        parcel->readBool(&hasWallpaper) ?:
-        parcel->readBool(&paused) ?:
-        parcel->readBool(&trustedOverlay) ?:
         parcel->readInt32(&touchOcclusionModeInt) ?:
         parcel->readInt32(&ownerPid) ?:
         parcel->readInt32(&ownerUid) ?:
@@ -177,6 +179,7 @@
     touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
 
     inputFeatures = Flags<Feature>(parcel->readInt32());
+    inputConfig = Flags<InputConfig>(parcel->readInt32());
     // clang-format off
     status = parcel->readInt32(&displayId) ?:
         applicationInfo.readFromParcel(parcel) ?:
diff --git a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
index e9a6a0c..9781ca9 100644
--- a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
+++ b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
@@ -17,7 +17,7 @@
 package android.gui;
 
 import android.gui.BitTube;
-import android.gui.VsyncEventData;
+import android.gui.ParcelableVsyncEventData;
 
 /** @hide */
 interface IDisplayEventConnection {
@@ -43,5 +43,5 @@
     /*
      * getLatestVsyncEventData() gets the latest vsync event data.
      */
-    VsyncEventData getLatestVsyncEventData();
+    ParcelableVsyncEventData getLatestVsyncEventData();
 }
diff --git a/libs/gui/aidl/android/gui/VsyncEventData.aidl b/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl
similarity index 89%
rename from libs/gui/aidl/android/gui/VsyncEventData.aidl
rename to libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl
index 7343515..ba76671 100644
--- a/libs/gui/aidl/android/gui/VsyncEventData.aidl
+++ b/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl
@@ -16,4 +16,4 @@
 
 package android.gui;
 
-parcelable VsyncEventData cpp_header "gui/VsyncEventData.h";
+parcelable ParcelableVsyncEventData cpp_header "gui/VsyncEventData.h";
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 4e04db3..cf7a4e5 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -35,6 +35,7 @@
 // ----------------------------------------------------------------------------
 
 using gui::IDisplayEventConnection;
+using gui::ParcelableVsyncEventData;
 using gui::VsyncEventData;
 
 namespace gui {
@@ -76,16 +77,7 @@
 
         struct VSync {
             uint32_t count;
-            nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
-            nsecs_t deadlineTimestamp __attribute__((aligned(8)));
-            nsecs_t frameInterval __attribute__((aligned(8)));
-            int64_t vsyncId;
-            size_t preferredFrameTimelineIndex __attribute__((aligned(8)));
-            struct FrameTimeline {
-                nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
-                nsecs_t deadlineTimestamp __attribute__((aligned(8)));
-                int64_t vsyncId;
-            } frameTimelines[VsyncEventData::kFrameTimelinesLength];
+            VsyncEventData vsyncData;
         };
 
         struct Hotplug {
@@ -175,13 +167,19 @@
     /**
      * getLatestVsyncEventData() gets the latest vsync event data.
      */
-    status_t getLatestVsyncEventData(VsyncEventData* outVsyncEventData) const;
+    status_t getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) const;
 
 private:
     sp<IDisplayEventConnection> mEventConnection;
     std::unique_ptr<gui::BitTube> mDataChannel;
+    std::optional<status_t> mInitError;
 };
 
+inline bool operator==(DisplayEventReceiver::Event::FrameRateOverride lhs,
+                       DisplayEventReceiver::Event::FrameRateOverride rhs) {
+    return (lhs.uid == rhs.uid) && std::abs(lhs.frameRateHz - rhs.frameRateHz) < 0.001f;
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/libs/gui/include/gui/HdrMetadata.h b/libs/gui/include/gui/HdrMetadata.h
index 1e9c3e7..0bdffac 100644
--- a/libs/gui/include/gui/HdrMetadata.h
+++ b/libs/gui/include/gui/HdrMetadata.h
@@ -44,6 +44,7 @@
     status_t unflatten(void const* buffer, size_t size);
 
     bool operator==(const HdrMetadata& rhs) const;
+    bool operator!=(const HdrMetadata& rhs) const { return !(*this == rhs); }
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 90b2a0e..0a59f52 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -52,6 +52,8 @@
 #include <unordered_set>
 #include <vector>
 
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+
 namespace android {
 
 struct client_cache_t;
@@ -533,15 +535,17 @@
      * displayToken
      *      The token of the display.
      * outSupport
-     *      An output parameter for whether the display supports
+     *      An output parameter for whether/how the display supports
      *      DISPLAY_DECORATION layers.
      *
      * Returns NO_ERROR upon success. Otherwise,
      *      NAME_NOT_FOUND if the display is invalid, or
      *      BAD_VALUE      if the output parameter is invalid.
      */
-    virtual status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
-                                                 bool* outSupport) const = 0;
+    virtual status_t getDisplayDecorationSupport(
+            const sp<IBinder>& displayToken,
+            std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                    outSupport) const = 0;
 
     /*
      * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 0df5822..a791c66 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -30,6 +30,7 @@
 #include <cstdint>
 #include <unordered_map>
 #include <unordered_set>
+#include <variant>
 
 namespace android {
 
@@ -130,12 +131,12 @@
     status_t readFromParcel(const Parcel* input) override;
 
     SurfaceStats() = default;
-    SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
-                 uint32_t hint, uint32_t currentMaxAcquiredBuffersCount,
-                 FrameEventHistoryStats frameEventStats, std::vector<JankData> jankData,
-                 ReleaseCallbackId previousReleaseCallbackId)
+    SurfaceStats(const sp<IBinder>& sc, std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
+                 const sp<Fence>& prevReleaseFence, uint32_t hint,
+                 uint32_t currentMaxAcquiredBuffersCount, FrameEventHistoryStats frameEventStats,
+                 std::vector<JankData> jankData, ReleaseCallbackId previousReleaseCallbackId)
           : surfaceControl(sc),
-            acquireTime(time),
+            acquireTimeOrFence(std::move(acquireTimeOrFence)),
             previousReleaseFence(prevReleaseFence),
             transformHint(hint),
             currentMaxAcquiredBufferCount(currentMaxAcquiredBuffersCount),
@@ -144,7 +145,7 @@
             previousReleaseCallbackId(previousReleaseCallbackId) {}
 
     sp<IBinder> surfaceControl;
-    nsecs_t acquireTime = -1;
+    std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
     sp<Fence> previousReleaseFence;
     uint32_t transformHint = 0;
     uint32_t currentMaxAcquiredBufferCount = 0;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 61eeab3..25637ef 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -47,6 +47,8 @@
 #include <gui/WindowInfosListenerReporter.h>
 #include <math/vec3.h>
 
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+
 namespace android {
 
 class HdrCapabilities;
@@ -58,12 +60,13 @@
 using gui::IRegionSamplingListener;
 
 struct SurfaceControlStats {
-    SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime,
+    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)
           : surfaceControl(sc),
             latchTime(latchTime),
-            acquireTime(acquireTime),
+            acquireTimeOrFence(std::move(acquireTimeOrFence)),
             presentFence(presentFence),
             previousReleaseFence(prevReleaseFence),
             transformHint(hint),
@@ -71,7 +74,7 @@
 
     sp<SurfaceControl> surfaceControl;
     nsecs_t latchTime = -1;
-    nsecs_t acquireTime = -1;
+    std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
     sp<Fence> presentFence;
     sp<Fence> previousReleaseFence;
     uint32_t transformHint = 0;
@@ -285,14 +288,16 @@
                                             float lightPosY, float lightPosZ, float lightRadius);
 
     /*
-     * Returns whether a display supports DISPLAY_DECORATION layers.
+     * Returns whether and how a display supports DISPLAY_DECORATION layers.
      *
      * displayToken
      *      The token of the display.
      *
-     * Returns whether a display supports DISPLAY_DECORATION layers.
+     * Returns how a display supports DISPLAY_DECORATION layers, or nullopt if
+     * it does not.
      */
-    static bool getDisplayDecorationSupport(const sp<IBinder>& displayToken);
+    static std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>
+    getDisplayDecorationSupport(const sp<IBinder>& displayToken);
 
     // ------------------------------------------------------------------------
     // surface creation / destruction
diff --git a/libs/gui/include/gui/VsyncEventData.h b/libs/gui/include/gui/VsyncEventData.h
index abac61c..8e99539 100644
--- a/libs/gui/include/gui/VsyncEventData.h
+++ b/libs/gui/include/gui/VsyncEventData.h
@@ -21,52 +21,44 @@
 #include <array>
 
 namespace android::gui {
-struct VsyncEventData : public Parcelable {
+// Plain Old Data (POD) vsync data structure. For example, it can be easily used in the
+// DisplayEventReceiver::Event union.
+struct VsyncEventData {
     // Max amount of frame timelines is arbitrarily set to be reasonable.
     static constexpr int64_t kFrameTimelinesLength = 7;
 
-    // The Vsync Id corresponsing to this vsync event. This will be used to
-    // populate ISurfaceComposer::setFrameTimelineVsync and
-    // SurfaceComposerClient::setFrameTimelineVsync
-    // TODO(b/198191703): Remove when JNI DisplayEventReceiver uses frameTimelines array.
-    int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
-
-    // The deadline in CLOCK_MONOTONIC that the app needs to complete its
-    // frame by (both on the CPU and the GPU)
-    // TODO(b/198191703): Remove when JNI DisplayEventReceiver uses frameTimelines array.
-    int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
-
     // The current frame interval in ns when this frame was scheduled.
-    int64_t frameInterval = 0;
+    int64_t frameInterval;
 
-    struct FrameTimeline : public Parcelable {
-        FrameTimeline() = default;
-        FrameTimeline(int64_t id, int64_t deadlineTimestamp, int64_t expectedPresentTime)
-              : id(id),
-                deadlineTimestamp(deadlineTimestamp),
-                expectedPresentTime(expectedPresentTime) {}
+    // Index into the frameTimelines that represents the platform's preferred frame timeline.
+    uint32_t preferredFrameTimelineIndex;
 
+    struct alignas(8) FrameTimeline {
         // The Vsync Id corresponsing to this vsync event. This will be used to
         // populate ISurfaceComposer::setFrameTimelineVsync and
         // SurfaceComposerClient::setFrameTimelineVsync
-        int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
+        int64_t vsyncId;
 
-        // The deadline in CLOCK_MONOTONIC that the app needs to complete its
-        // frame by (both on the CPU and the GPU)
-        int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+        // The deadline in CLOCK_MONOTONIC in nanos that the app needs to complete its
+        // frame by (both on the CPU and the GPU).
+        int64_t deadlineTimestamp;
 
-        // The anticipated Vsync present time.
-        int64_t expectedPresentTime = 0;
+        // The anticipated Vsync presentation time in nanos.
+        int64_t expectedPresentationTime;
+    } frameTimelines[kFrameTimelinesLength]; // Sorted possible frame timelines.
 
-        status_t readFromParcel(const Parcel*) override;
-        status_t writeToParcel(Parcel*) const override;
-    };
+    // Gets the preferred frame timeline's vsync ID.
+    int64_t preferredVsyncId() const;
 
-    // Sorted possible frame timelines.
-    std::array<FrameTimeline, kFrameTimelinesLength> frameTimelines;
+    // Gets the preferred frame timeline's deadline timestamp.
+    int64_t preferredDeadlineTimestamp() const;
 
-    // Index into the frameTimelines that represents the platform's preferred frame timeline.
-    size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max();
+    // Gets the preferred frame timeline's expected vsync timestamp.
+    int64_t preferredExpectedPresentationTime() const;
+};
+
+struct ParcelableVsyncEventData : public Parcelable {
+    VsyncEventData vsync;
 
     status_t readFromParcel(const Parcel*) override;
     status_t writeToParcel(Parcel*) const override;
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index eb64ac9..b9bffaa 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -151,6 +151,33 @@
         // clang-format on
     };
 
+    // Flags used to determine configuration of this input window.
+    // Input windows can be configured with two sets of flags: InputFeature (WindowInfo::Feature
+    // defined above), and InputConfig. When adding a new configuration for an input window:
+    //   - If you are adding a new flag that's visible and accessible to apps, it should be added
+    //   as an InputFeature.
+    //   - If you are adding an internal behaviour that is used within the system or shell and is
+    //   not exposed to apps, it should be added as an InputConfig.
+    enum class InputConfig : uint32_t {
+        // clang-format off
+        NONE                         = 0,
+        NOT_VISIBLE                  = 1 << 0,
+        NOT_FOCUSABLE                = 1 << 1,
+        NOT_TOUCHABLE                = 1 << 2,
+        PREVENT_SPLITTING            = 1 << 3,
+        DUPLICATE_TOUCH_TO_WALLPAPER = 1 << 4,
+        IS_WALLPAPER                 = 1 << 5,
+        PAUSE_DISPATCHING            = 1 << 6,
+        // This flag is set when the window is of a trusted type that is allowed to silently
+        // overlay other windows for the purpose of implementing the secure views feature.
+        // Trusted overlays, such as IME windows, can partly obscure other windows without causing
+        // motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+        TRUSTED_OVERLAY              = 1 << 7,
+        WATCH_OUTSIDE_TOUCH          = 1 << 8,
+        SLIPPERY                     = 1 << 9,
+        // clang-format on
+    };
+
     /* These values are filled in by the WM and passed through SurfaceFlinger
      * unless specified otherwise.
      */
@@ -164,8 +191,6 @@
     // This uniquely identifies the input window.
     int32_t id = -1;
     std::string name;
-    Flags<Flag> flags;
-    Type type = Type::UNKNOWN;
     std::chrono::nanoseconds dispatchingTimeout = std::chrono::seconds(5);
 
     /* These values are filled in by SurfaceFlinger. */
@@ -198,26 +223,24 @@
      * to absolute coordinates by SurfaceFlinger once the frame is computed.
      */
     Region touchableRegion;
-    bool visible = false;
-    bool focusable = false;
-    bool hasWallpaper = false;
-    bool paused = false;
-    /* This flag is set when the window is of a trusted type that is allowed to silently
-     * overlay other windows for the purpose of implementing the secure views feature.
-     * Trusted overlays, such as IME windows, can partly obscure other windows without causing
-     * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
-     */
-    bool trustedOverlay = false;
+
     TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED;
     int32_t ownerPid = -1;
     int32_t ownerUid = -1;
     std::string packageName;
     Flags<Feature> inputFeatures;
+    Flags<InputConfig> inputConfig;
     int32_t displayId = ADISPLAY_ID_NONE;
     InputApplicationInfo applicationInfo;
     bool replaceTouchableRegionWithCrop = false;
     wp<IBinder> touchableRegionCropHandle;
 
+    // The window's layout params flags and type set by WM.
+    Type layoutParamsType = Type::UNKNOWN;
+    Flags<Flag> layoutParamsFlags;
+
+    void setInputConfig(Flags<InputConfig> config, bool value);
+
     void addTouchableRegion(const Rect& region);
 
     bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index 6403208..62d1496 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -134,12 +134,15 @@
 
         void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
                                        nsecs_t latchTime) const {
-            const auto& [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
-                         transformHint, frameEvents] = surfaceControlStats;
+            const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence,
+                         previousReleaseFence, transformHint, frameEvents] = surfaceControlStats;
 
-            ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
+            ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence));
+            ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0,
+                      mBufferResult == ExpectedResult::Buffer::ACQUIRED)
                     << "bad acquire time";
-            ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
+            ASSERT_LE(std::get<nsecs_t>(acquireTimeOrFence), latchTime)
+                    << "acquire time should be <= latch time";
 
             if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
                 ASSERT_NE(previousReleaseFence, nullptr)
diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp
index bddb0ac..cc33e4c 100644
--- a/libs/gui/sysprop/Android.bp
+++ b/libs/gui/sysprop/Android.bp
@@ -16,4 +16,9 @@
     cpp: {
         min_sdk_version: "29",
     },
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+        "test_com.android.media.swcodec",
+    ],
 }
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index bcd39db..da88463 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -20,9 +20,10 @@
 namespace android::test {
 
 #define CHECK_OFFSET(type, member, expected_offset) \
-    static_assert((offsetof(type, member) == (expected_offset)), "")
+    static_assert((offsetof(type, member) == (expected_offset)))
 
 TEST(DisplayEventStructLayoutTest, TestEventAlignment) {
+    static_assert(std::is_pod<DisplayEventReceiver::Event::VSync>::value);
     CHECK_OFFSET(DisplayEventReceiver::Event, vsync, 24);
     CHECK_OFFSET(DisplayEventReceiver::Event, hotplug, 24);
     CHECK_OFFSET(DisplayEventReceiver::Event, modeChange, 24);
@@ -32,10 +33,29 @@
     CHECK_OFFSET(DisplayEventReceiver::Event::Header, timestamp, 16);
 
     CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
-    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8);
-    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, deadlineTimestamp, 16);
-    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, frameInterval, 24);
-    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncId, 32);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameInterval, 8);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.preferredFrameTimelineIndex, 16);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 24);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 24);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].deadlineTimestamp,
+                 32);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
+                 vsyncData.frameTimelines[0].expectedPresentationTime, 40);
+    // Also test the offsets of the last frame timeline. A loop is not used because the non-const
+    // index cannot be used in static_assert.
+    const int lastFrameTimelineOffset = /* Start of array */ 24 +
+            (VsyncEventData::kFrameTimelinesLength - 1) * /* Size of FrameTimeline */ 24;
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
+                 vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1].vsyncId,
+                 lastFrameTimelineOffset);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
+                 vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1]
+                         .deadlineTimestamp,
+                 lastFrameTimelineOffset + 8);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
+                 vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1]
+                         .expectedPresentationTime,
+                 lastFrameTimelineOffset + 16);
 
     CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0);
 
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 06a0aca..fcfe21b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -266,14 +266,8 @@
     void populateInputInfo(int width, int height) {
         mInputInfo.token = mClientChannel->getConnectionToken();
         mInputInfo.name = "Test info";
-        mInputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL;
-        mInputInfo.type = WindowInfo::Type::BASE_APPLICATION;
         mInputInfo.dispatchingTimeout = 5s;
         mInputInfo.globalScaleFactor = 1.0;
-        mInputInfo.focusable = true;
-        mInputInfo.hasWallpaper = false;
-        mInputInfo.paused = false;
-
         mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
 
         InputApplicationInfo aInfo;
@@ -750,7 +744,7 @@
     // Add non touchable window to fully cover touchable window. Window behind gets touch, but
     // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     nonTouchableSurface->mInputInfo.ownerUid = 22222;
     // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
     // the default obscured/untrusted touch filter introduced in S.
@@ -770,8 +764,8 @@
     // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
-    parentSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+    parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     nonTouchableSurface->mInputInfo.ownerUid = 22222;
     parentSurface->mInputInfo.ownerUid = 22222;
     nonTouchableSurface->showAt(0, 0);
@@ -794,8 +788,8 @@
     // the touchable window. Window behind gets touch with no obscured flags.
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
-    parentSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
+    parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     nonTouchableSurface->mInputInfo.ownerUid = 22222;
     parentSurface->mInputInfo.ownerUid = 22222;
     nonTouchableSurface->showAt(0, 0);
@@ -815,7 +809,7 @@
 
     std::unique_ptr<InputSurface> bufferSurface =
             InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
-    bufferSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     bufferSurface->mInputInfo.ownerUid = 22222;
 
     surface->showAt(10, 10);
@@ -830,7 +824,7 @@
 
     std::unique_ptr<BlastInputSurface> bufferSurface =
             BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
-    bufferSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     bufferSurface->mInputInfo.ownerUid = 22222;
 
     surface->showAt(10, 10);
@@ -883,7 +877,7 @@
             [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
     surface->showAt(100, 100);
     std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
-    obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     obscuringSurface->mInputInfo.ownerUid = 22222;
     obscuringSurface->showAt(100, 100);
     injectTap(101, 101);
@@ -902,7 +896,7 @@
             [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
     surface->showAt(100, 100);
     std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
-    obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
     obscuringSurface->mInputInfo.ownerUid = 22222;
     obscuringSurface->showAt(190, 190);
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 120ed48..6056280 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -47,6 +47,7 @@
 // retrieve wide-color and hdr settings from configstore
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
+using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
 using gui::IDisplayEventConnection;
 using gui::IRegionSamplingListener;
 using ui::ColorMode;
@@ -889,8 +890,9 @@
         return NO_ERROR;
     }
 
-    status_t getDisplayDecorationSupport(const sp<IBinder>& /*displayToken*/,
-                                         bool* /*outSupport*/) const override {
+    status_t getDisplayDecorationSupport(
+            const sp<IBinder>& /*displayToken*/,
+            std::optional<DisplayDecorationSupport>* /*outSupport*/) const override {
         return NO_ERROR;
     }
 
diff --git a/libs/gui/tests/VsyncEventData_test.cpp b/libs/gui/tests/VsyncEventData_test.cpp
index a670d42..f114522 100644
--- a/libs/gui/tests/VsyncEventData_test.cpp
+++ b/libs/gui/tests/VsyncEventData_test.cpp
@@ -22,54 +22,37 @@
 
 namespace android {
 
+using gui::ParcelableVsyncEventData;
 using gui::VsyncEventData;
 using FrameTimeline = gui::VsyncEventData::FrameTimeline;
 
 namespace test {
 
-TEST(VsyncEventData, Parcelling) {
-    VsyncEventData data;
-    data.id = 123;
-    data.deadlineTimestamp = 456;
-    data.frameInterval = 789;
-    data.preferredFrameTimelineIndex = 1;
-    FrameTimeline timeline0 = FrameTimeline(1, 2, 3);
-    FrameTimeline timeline1 = FrameTimeline(4, 5, 6);
-    data.frameTimelines[0] = timeline0;
-    data.frameTimelines[1] = timeline1;
+TEST(ParcelableVsyncEventData, Parcelling) {
+    ParcelableVsyncEventData data;
+    data.vsync.frameInterval = 789;
+    data.vsync.preferredFrameTimelineIndex = 1;
+    FrameTimeline timeline0 = FrameTimeline{1, 2, 3};
+    FrameTimeline timeline1 = FrameTimeline{4, 5, 6};
+    data.vsync.frameTimelines[0] = timeline0;
+    data.vsync.frameTimelines[1] = timeline1;
 
     Parcel p;
     data.writeToParcel(&p);
     p.setDataPosition(0);
 
-    VsyncEventData data2;
+    ParcelableVsyncEventData data2;
     data2.readFromParcel(&p);
-    ASSERT_EQ(data.id, data2.id);
-    ASSERT_EQ(data.deadlineTimestamp, data2.deadlineTimestamp);
-    ASSERT_EQ(data.frameInterval, data2.frameInterval);
-    ASSERT_EQ(data.preferredFrameTimelineIndex, data2.preferredFrameTimelineIndex);
-    for (int i = 0; i < data.frameTimelines.size(); i++) {
-        ASSERT_EQ(data.frameTimelines[i].id, data2.frameTimelines[i].id);
-        ASSERT_EQ(data.frameTimelines[i].deadlineTimestamp,
-                  data2.frameTimelines[i].deadlineTimestamp);
-        ASSERT_EQ(data.frameTimelines[i].expectedPresentTime,
-                  data2.frameTimelines[i].expectedPresentTime);
+    ASSERT_EQ(data.vsync.frameInterval, data2.vsync.frameInterval);
+    ASSERT_EQ(data.vsync.preferredFrameTimelineIndex, data2.vsync.preferredFrameTimelineIndex);
+    for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+        ASSERT_EQ(data.vsync.frameTimelines[i].vsyncId, data2.vsync.frameTimelines[i].vsyncId);
+        ASSERT_EQ(data.vsync.frameTimelines[i].deadlineTimestamp,
+                  data2.vsync.frameTimelines[i].deadlineTimestamp);
+        ASSERT_EQ(data.vsync.frameTimelines[i].expectedPresentationTime,
+                  data2.vsync.frameTimelines[i].expectedPresentationTime);
     }
 }
 
-TEST(FrameTimeline, Parcelling) {
-    FrameTimeline timeline = FrameTimeline(1, 2, 3);
-
-    Parcel p;
-    timeline.writeToParcel(&p);
-    p.setDataPosition(0);
-
-    FrameTimeline timeline2;
-    timeline2.readFromParcel(&p);
-    ASSERT_EQ(timeline.id, timeline2.id);
-    ASSERT_EQ(timeline.deadlineTimestamp, timeline2.deadlineTimestamp);
-    ASSERT_EQ(timeline.expectedPresentTime, timeline2.expectedPresentTime);
-}
-
 } // namespace test
 } // namespace android
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index ff3ba2a..ff9bae2 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -49,8 +49,8 @@
     i.windowToken = new BBinder();
     i.id = 1;
     i.name = "Foobar";
-    i.flags = WindowInfo::Flag::SLIPPERY;
-    i.type = WindowInfo::Type::INPUT_METHOD;
+    i.layoutParamsFlags = WindowInfo::Flag::SLIPPERY;
+    i.layoutParamsType = WindowInfo::Type::INPUT_METHOD;
     i.dispatchingTimeout = 12s;
     i.frameLeft = 93;
     i.frameTop = 34;
@@ -60,15 +60,12 @@
     i.globalScaleFactor = 0.3;
     i.alpha = 0.7;
     i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
-    i.visible = false;
-    i.focusable = false;
-    i.hasWallpaper = false;
-    i.paused = false;
     i.touchOcclusionMode = TouchOcclusionMode::ALLOW;
     i.ownerPid = 19;
     i.ownerUid = 24;
     i.packageName = "com.example.package";
     i.inputFeatures = WindowInfo::Feature::DISABLE_USER_ACTIVITY;
+    i.inputConfig = WindowInfo::InputConfig::NOT_FOCUSABLE;
     i.displayId = 34;
     i.replaceTouchableRegionWithCrop = true;
     i.touchableRegionCropHandle = touchableRegionCropHandle;
@@ -85,8 +82,8 @@
     ASSERT_EQ(i.windowToken, i2.windowToken);
     ASSERT_EQ(i.id, i2.id);
     ASSERT_EQ(i.name, i2.name);
-    ASSERT_EQ(i.flags, i2.flags);
-    ASSERT_EQ(i.type, i2.type);
+    ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
+    ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
     ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
     ASSERT_EQ(i.frameLeft, i2.frameLeft);
     ASSERT_EQ(i.frameTop, i2.frameTop);
@@ -96,15 +93,12 @@
     ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
     ASSERT_EQ(i.alpha, i2.alpha);
     ASSERT_EQ(i.transform, i2.transform);
-    ASSERT_EQ(i.visible, i2.visible);
-    ASSERT_EQ(i.focusable, i2.focusable);
-    ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
-    ASSERT_EQ(i.paused, i2.paused);
     ASSERT_EQ(i.touchOcclusionMode, i2.touchOcclusionMode);
     ASSERT_EQ(i.ownerPid, i2.ownerPid);
     ASSERT_EQ(i.ownerUid, i2.ownerUid);
     ASSERT_EQ(i.packageName, i2.packageName);
     ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
+    ASSERT_EQ(i.inputConfig, i2.inputConfig);
     ASSERT_EQ(i.displayId, i2.displayId);
     ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
     ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
diff --git a/libs/nativebase/Android.bp b/libs/nativebase/Android.bp
index 1a4729c..77b23db 100644
--- a/libs/nativebase/Android.bp
+++ b/libs/nativebase/Android.bp
@@ -45,5 +45,9 @@
             enabled: true,
         },
     },
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+    ],
     min_sdk_version: "29",
 }
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index b182a4a..a3eebdd 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -78,7 +78,7 @@
 struct FrameCallback {
     AChoreographer_frameCallback callback;
     AChoreographer_frameCallback64 callback64;
-    AChoreographer_extendedFrameCallback extendedCallback;
+    AChoreographer_vsyncCallback vsyncCallback;
     void* data;
     nsecs_t dueTime;
 
@@ -103,9 +103,7 @@
 struct ChoreographerFrameCallbackDataImpl {
     int64_t frameTimeNanos{0};
 
-    std::array<VsyncEventData::FrameTimeline, VsyncEventData::kFrameTimelinesLength> frameTimelines;
-
-    size_t preferredFrameTimelineIndex;
+    VsyncEventData vsyncEventData;
 
     const Choreographer* choreographer;
 };
@@ -123,7 +121,7 @@
     explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
     void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                   AChoreographer_frameCallback64 cb64,
-                                  AChoreographer_extendedFrameCallback extendedCallback, void* data,
+                                  AChoreographer_vsyncCallback vsyncCallback, void* data,
                                   nsecs_t delay);
     void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
             EXCLUDES(gChoreographers.lock);
@@ -232,10 +230,10 @@
 
 void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                              AChoreographer_frameCallback64 cb64,
-                                             AChoreographer_extendedFrameCallback extendedCallback,
-                                             void* data, nsecs_t delay) {
+                                             AChoreographer_vsyncCallback vsyncCallback, void* data,
+                                             nsecs_t delay) {
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    FrameCallback callback{cb, cb64, extendedCallback, data, now + delay};
+    FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
     {
         std::lock_guard<std::mutex> _l{mLock};
         mFrameCallbacks.push(callback);
@@ -391,13 +389,13 @@
     }
     mLastVsyncEventData = vsyncEventData;
     for (const auto& cb : callbacks) {
-        if (cb.extendedCallback != nullptr) {
+        if (cb.vsyncCallback != nullptr) {
             const ChoreographerFrameCallbackDataImpl frameCallbackData =
                     createFrameCallbackData(timestamp);
             mInCallback = true;
-            cb.extendedCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
-                                        &frameCallbackData),
-                                cb.data);
+            cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
+                                     &frameCallbackData),
+                             cb.data);
             mInCallback = false;
         } else if (cb.callback64 != nullptr) {
             cb.callback64(timestamp, cb.data);
@@ -450,8 +448,7 @@
 
 ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
     return {.frameTimeNanos = timestamp,
-            .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex,
-            .frameTimelines = mLastVsyncEventData.frameTimelines,
+            .vsyncEventData = mLastVsyncEventData,
             .choreographer = this};
 }
 
@@ -525,10 +522,9 @@
                                                     void* data, uint32_t delayMillis) {
     return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
 }
-void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
-                                                   AChoreographer_extendedFrameCallback callback,
-                                                   void* data) {
-    return AChoreographer_postExtendedFrameCallback(choreographer, callback, data);
+void AChoreographer_routePostVsyncCallback(AChoreographer* choreographer,
+                                           AChoreographer_vsyncCallback callback, void* data) {
+    return AChoreographer_postVsyncCallback(choreographer, callback, data);
 }
 void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
                                                      AChoreographer_refreshRateCallback callback,
@@ -556,9 +552,10 @@
         const AChoreographerFrameCallbackData* data, size_t index) {
     return AChoreographerFrameCallbackData_getFrameTimelineVsyncId(data, index);
 }
-int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentationTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(data, index);
+    return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(data,
+                                                                                         index);
 }
 int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
@@ -592,9 +589,8 @@
     AChoreographer_to_Choreographer(choreographer)
             ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
 }
-void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
-                                              AChoreographer_extendedFrameCallback callback,
-                                              void* data) {
+void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
+                                      AChoreographer_vsyncCallback callback, void* data) {
     AChoreographer_to_Choreographer(choreographer)
             ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
 }
@@ -634,7 +630,7 @@
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    return frameCallbackData->frameTimelines.size();
+    return VsyncEventData::kFrameTimelinesLength;
 }
 size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
         const AChoreographerFrameCallbackData* data) {
@@ -642,7 +638,7 @@
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    return frameCallbackData->preferredFrameTimelineIndex;
+    return frameCallbackData->vsyncEventData.preferredFrameTimelineIndex;
 }
 AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
         const AChoreographerFrameCallbackData* data, size_t index) {
@@ -650,17 +646,17 @@
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
-    return frameCallbackData->frameTimelines[index].id;
+    LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+    return frameCallbackData->vsyncEventData.frameTimelines[index].vsyncId;
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
     const ChoreographerFrameCallbackDataImpl* frameCallbackData =
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
-    return frameCallbackData->frameTimelines[index].expectedPresentTime;
+    LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+    return frameCallbackData->vsyncEventData.frameTimelines[index].expectedPresentationTime;
 }
 int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
@@ -668,8 +664,8 @@
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
-    return frameCallbackData->frameTimelines[index].deadlineTimestamp;
+    LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+    return frameCallbackData->vsyncEventData.frameTimelines[index].deadlineTimestamp;
 }
 
 AChoreographer* AChoreographer_create() {
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index d650c26..19be5bd 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -50,9 +50,8 @@
 void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
                                                     AChoreographer_frameCallback64 callback,
                                                     void* data, uint32_t delayMillis);
-void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
-                                                   AChoreographer_extendedFrameCallback callback,
-                                                   void* data);
+void AChoreographer_routePostVsyncCallback(AChoreographer* choreographer,
+                                           AChoreographer_vsyncCallback callback, void* data);
 void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
                                                      AChoreographer_refreshRateCallback callback,
                                                      void* data);
@@ -67,7 +66,7 @@
         const AChoreographerFrameCallbackData* data);
 AVsyncId AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
         const AChoreographerFrameCallbackData* data, size_t index);
-int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentationTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index);
 int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index);
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 6579313..2da10cd 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -7,12 +7,12 @@
     AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
     AChoreographer_registerRefreshRateCallback; # apex # introduced=30
     AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
-    AChoreographer_postExtendedFrameCallback; # apex # introduced=33
+    AChoreographer_postVsyncCallback; # apex # introduced=33
     AChoreographerFrameCallbackData_getFrameTimeNanos; # apex # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelinesLength; # apex # introduced=33
     AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # apex # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # apex # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos; # apex # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos; # apex # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos; # apex # introduced=33
     AChoreographer_create; # apex # introduced=30
     AChoreographer_destroy; # apex # introduced=30
@@ -35,12 +35,12 @@
       android::AChoreographer_routePostFrameCallbackDelayed64*;
       android::AChoreographer_routeRegisterRefreshRateCallback*;
       android::AChoreographer_routeUnregisterRefreshRateCallback*;
-      android::AChoreographer_routePostExtendedFrameCallback*;
+      android::AChoreographer_routePostVsyncCallback*;
       android::AChoreographerFrameCallbackData_routeGetFrameTimeNanos*;
       android::AChoreographerFrameCallbackData_routeGetFrameTimelinesLength*;
       android::AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex*;
       android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*;
-      android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos*;
+      android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentationTimeNanos*;
       android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos*;
       android::AChoreographer_signalRefreshRateCallbacks*;
       android::AChoreographer_getFrameInterval*;
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 2578ee8..576941f 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -353,12 +353,12 @@
         return INVALID_OPERATION;
     }
 
-    GraphicBuffer* gBuffer = new GraphicBuffer();
+    sp<GraphicBuffer> gBuffer(new GraphicBuffer());
     status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount);
     if (err != NO_ERROR) {
         return err;
     }
-    *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer);
+    *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer.get());
     // Ensure the buffer has a positive ref-count.
     AHardwareBuffer_acquire(*outBuffer);
 
@@ -584,6 +584,8 @@
             "HAL and AHardwareBuffer pixel format don't match");
     static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
             "HAL and AHardwareBuffer pixel format don't match");
+    static_assert(HAL_PIXEL_FORMAT_YCBCR_P010 == AHARDWAREBUFFER_FORMAT_YCbCr_P010,
+            "HAL and AHardwareBuffer pixel format don't match");
     static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
                           AHARDWAREBUFFER_FORMAT_R8_UNORM,
             "HAL and AHardwareBuffer pixel format don't match");
@@ -617,6 +619,7 @@
         case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
         case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
         case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+        case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
             return true;
 
         default:
@@ -633,6 +636,7 @@
         case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
         case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
         case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+        case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
             return true;
         default:
             return false;
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 9286009..d30efa1 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -47,6 +47,11 @@
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
     min_sdk_version: "29",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+        "test_com.android.media.swcodec",
+    ],
     host_supported: true,
 }
 
@@ -64,7 +69,7 @@
         symbol_file: "libnativewindow.map.txt",
         unversioned: true,
         override_export_include_dirs: [
-            "include"
+            "include",
         ],
     },
     export_include_dirs: [
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 6f1f04d..c35507b 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -160,6 +160,14 @@
     AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420             = 0x23,
 
     /**
+     * YUV P010 format.
+     * Must have an even width and height. Can be accessed in OpenGL
+     * shaders through an external sampler. Does not support mip-maps
+     * cube-maps or multi-layered textures.
+     */
+    AHARDWAREBUFFER_FORMAT_YCbCr_P010               = 0x36,
+
+    /**
      * Corresponding formats:
      *   Vulkan: VK_FORMAT_R8_UNORM
      *   OpenGL ES: GR_GL_R8
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 2c51ccd..40ba5ad 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -54,6 +54,10 @@
     // dataspace, in non-linear space.
     mat4 colorTransform = mat4();
 
+    // If true, and colorTransform is non-identity, most client draw calls can
+    // ignore it. Some draws (e.g. screen decorations) may need it, though.
+    bool deviceHandlesColorTransform = false;
+
     // An additional orientation flag to be applied after clipping the output.
     // By way of example, this may be used for supporting fullscreen screenshot
     // capture of a device in landscape while the buffer is in portrait
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 763b82d..b467b35 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -792,7 +792,7 @@
 
     // setup color filter if necessary
     sk_sp<SkColorFilter> displayColorTransform;
-    if (display.colorTransform != mat4()) {
+    if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) {
         displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
     }
     const bool ctModifiesAlpha =
@@ -1107,11 +1107,37 @@
 
             if (imageTextureRef->colorType() == kAlpha_8_SkColorType) {
                 LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8");
-                float matrix[] = { 0, 0, 0, 0, 0,
-                                   0, 0, 0, 0, 0,
-                                   0, 0, 0, 0, 0,
-                                   0, 0, 0, -1, 1 };
-                paint.setColorFilter(SkColorFilters::Matrix(matrix));
+
+                // SysUI creates the alpha layer as a coverage layer, which is
+                // appropriate for the DPU. Use a color matrix to convert it to
+                // a mask.
+                // TODO (b/219525258): Handle input as a mask.
+                //
+                // The color matrix will convert A8 pixels with no alpha to
+                // black, as described by this vector. If the display handles
+                // the color transform, we need to invert it to find the color
+                // that will result in black after the DPU applies the transform.
+                SkV4 black{0.0f, 0.0f, 0.0f, 1.0f}; // r, g, b, a
+                if (display.colorTransform != mat4() && display.deviceHandlesColorTransform) {
+                    SkM44 colorSpaceMatrix = getSkM44(display.colorTransform);
+                    if (colorSpaceMatrix.invert(&colorSpaceMatrix)) {
+                        black = colorSpaceMatrix * black;
+                    } else {
+                        // We'll just have to use 0,0,0 as black, which should
+                        // be close to correct.
+                        ALOGI("Could not invert colorTransform!");
+                    }
+                }
+                SkColorMatrix colorMatrix(0, 0, 0, 0, black[0],
+                                          0, 0, 0, 0, black[1],
+                                          0, 0, 0, 0, black[2],
+                                          0, 0, 0, -1, 1);
+                if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) {
+                    // On the other hand, if the device doesn't handle it, we
+                    // have to apply it ourselves.
+                    colorMatrix.postConcat(toSkColorMatrix(display.colorTransform));
+                }
+                paint.setColorFilter(SkColorFilters::Matrix(colorMatrix));
             }
         } else {
             ATRACE_NAME("DrawColor");
@@ -1134,8 +1160,8 @@
             paint.setBlendMode(SkBlendMode::kSrc);
         }
 
-        // A color filter will have been set for an A8 buffer. Do not replace
-        // it with the displayColorTransform, which shouldn't affect A8.
+        // An A8 buffer will already have the proper color filter attached to
+        // its paint, including the displayColorTransform as needed.
         if (!paint.getColorFilter()) {
             paint.setColorFilter(displayColorTransform);
         }
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index e197150..add7a94 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -2627,6 +2627,7 @@
 
     const auto r8Buffer = allocateR8Buffer(2, 1);
     if (!r8Buffer) {
+        GTEST_SKIP() << "Test is only necessary on devices that support r8";
         return;
     }
     {
@@ -2677,6 +2678,144 @@
     expectBufferColor(Rect(0, 0, 1, 1), 0,   0, 0, 255);
     expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255);
 }
+
+TEST_P(RenderEngineTest, r8_respects_color_transform) {
+    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+
+    const auto r8Buffer = allocateR8Buffer(2, 1);
+    if (!r8Buffer) {
+        GTEST_SKIP() << "Test is only necessary on devices that support r8";
+        return;
+    }
+    {
+        uint8_t* pixels;
+        r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                    reinterpret_cast<void**>(&pixels));
+        pixels[0] = 0;
+        pixels[1] = 255;
+        r8Buffer->getBuffer()->unlock();
+    }
+
+    const auto rect = Rect(0, 0, 2, 1);
+    const renderengine::DisplaySettings display{
+            .physicalDisplay = rect,
+            .clip = rect,
+            .outputDataspace = ui::Dataspace::SRGB,
+            // Verify that the R8 layer respects the color transform when
+            // deviceHandlesColorTransform is false. This transform converts
+            // pure red to pure green. That will occur when the R8 buffer is
+            // 255. When the R8 buffer is 0, it will still change to black, as
+            // with r8_behaves_as_mask.
+            .colorTransform = mat4(0, 1, 0, 0,
+                                   0, 0, 0, 0,
+                                   0, 0, 1, 0,
+                                   0, 0, 0, 1),
+            .deviceHandlesColorTransform = false,
+    };
+
+    const auto redBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(255, 0, 0, 255));
+    const renderengine::LayerSettings redLayer{
+            .geometry.boundaries = rect.toFloatRect(),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = redBuffer,
+                                    },
+                    },
+            .alpha = 1.0f,
+    };
+    const renderengine::LayerSettings r8Layer{
+            .geometry.boundaries = rect.toFloatRect(),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = r8Buffer,
+                                    },
+                    },
+            .alpha = 1.0f,
+    };
+
+    std::vector<renderengine::LayerSettings> layers{redLayer, r8Layer};
+    invokeDraw(display, layers);
+
+    expectBufferColor(Rect(0, 0, 1, 1), 0,   0, 0, 255);
+    expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255);
+}
+
+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);
+    if (!r8Buffer) {
+        GTEST_SKIP() << "Test is only necessary on devices that support r8";
+        return;
+    }
+    {
+        uint8_t* pixels;
+        r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                    reinterpret_cast<void**>(&pixels));
+        pixels[0] = 0;
+        pixels[1] = 255;
+        r8Buffer->getBuffer()->unlock();
+    }
+
+    const auto rect = Rect(0, 0, 2, 1);
+    const renderengine::DisplaySettings display{
+            .physicalDisplay = rect,
+            .clip = rect,
+            .outputDataspace = ui::Dataspace::SRGB,
+            // If deviceHandlesColorTransform is true, pixels where the A8
+            // buffer is opaque are unaffected. If the colorTransform is
+            // invertible, pixels where the A8 buffer are transparent have the
+            // inverse applied to them so that the DPU will convert them back to
+            // black. Test with an arbitrary, invertible matrix.
+            .colorTransform = mat4(1, 0, 0, 2,
+                                   3, 1, 2, 5,
+                                   0, 5, 3, 0,
+                                   0, 1, 0, 2),
+            .deviceHandlesColorTransform = true,
+    };
+
+    const auto redBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(255, 0, 0, 255));
+    const renderengine::LayerSettings redLayer{
+            .geometry.boundaries = rect.toFloatRect(),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = redBuffer,
+                                    },
+                    },
+            .alpha = 1.0f,
+    };
+    const renderengine::LayerSettings r8Layer{
+            .geometry.boundaries = rect.toFloatRect(),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = r8Buffer,
+                                    },
+                    },
+            .alpha = 1.0f,
+    };
+
+    std::vector<renderengine::LayerSettings> layers{redLayer, r8Layer};
+    invokeDraw(display, layers);
+
+    expectBufferColor(Rect(1, 0, 2, 1), 255, 0, 0, 255); // Still red.
+    expectBufferColor(Rect(0, 0, 1, 1), 0,  70, 0, 255);
+}
 } // namespace renderengine
 } // namespace android
 
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index ef3ceda..2be98e7 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -55,12 +55,12 @@
     return service;
 }
 
-bool SensorPrivacyManager::supportsSensorToggle(int sensor) {
+bool SensorPrivacyManager::supportsSensorToggle(int toggleType, int sensor) {
     if (mSupportedCache.find(sensor) == mSupportedCache.end()) {
         sp<hardware::ISensorPrivacyManager> service = getService();
         if (service != nullptr) {
             bool result;
-            service->supportsSensorToggle(sensor, &result);
+            service->supportsSensorToggle(toggleType, sensor, &result);
             mSupportedCache[sensor] = result;
             return result;
         }
@@ -80,12 +80,12 @@
     }
 }
 
-status_t SensorPrivacyManager::addIndividualSensorPrivacyListener(int userId, int sensor,
+status_t SensorPrivacyManager::addToggleSensorPrivacyListener(
         const sp<hardware::ISensorPrivacyListener>& listener)
 {
     sp<hardware::ISensorPrivacyManager> service = getService();
     if (service != nullptr) {
-        return service->addIndividualSensorPrivacyListener(userId, sensor, listener)
+        return service->addToggleSensorPrivacyListener(listener)
                 .transactionError();
     }
     return UNEXPECTED_NULL;
@@ -100,12 +100,12 @@
     }
 }
 
-void SensorPrivacyManager::removeIndividualSensorPrivacyListener(int sensor,
+void SensorPrivacyManager::removeToggleSensorPrivacyListener(
         const sp<hardware::ISensorPrivacyListener>& listener)
 {
     sp<hardware::ISensorPrivacyManager> service = getService();
     if (service != nullptr) {
-        service->removeIndividualSensorPrivacyListener(sensor, listener);
+        service->removeToggleSensorPrivacyListener(listener);
     }
 }
 
@@ -121,24 +121,36 @@
     return false;
 }
 
-bool SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor)
+bool SensorPrivacyManager::isToggleSensorPrivacyEnabled(int sensor)
 {
-    sp<hardware::ISensorPrivacyManager> service = getService();
+	sp<hardware::ISensorPrivacyManager> service = getService();
     if (service != nullptr) {
         bool result;
-        service->isIndividualSensorPrivacyEnabled(userId, sensor, &result);
+        service->isCombinedToggleSensorPrivacyEnabled(sensor, &result);
         return result;
     }
     // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
     return false;
 }
 
-status_t SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor,
+bool SensorPrivacyManager::isToggleSensorPrivacyEnabled(int toggleType, int sensor)
+{
+    sp<hardware::ISensorPrivacyManager> service = getService();
+    if (service != nullptr) {
+        bool result;
+        service->isToggleSensorPrivacyEnabled(toggleType, sensor, &result);
+        return result;
+    }
+    // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
+    return false;
+}
+
+status_t SensorPrivacyManager::isToggleSensorPrivacyEnabled(int toggleType, int sensor,
         bool &returnVal)
 {
     sp<hardware::ISensorPrivacyManager> service = getService();
     if (service != nullptr) {
-        binder::Status res = service->isIndividualSensorPrivacyEnabled(userId, sensor, &returnVal);
+        binder::Status res = service->isToggleSensorPrivacyEnabled(toggleType, sensor, &returnVal);
         return res.transactionError();
     }
     // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
index 58177d8..eccd54c 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
@@ -20,5 +20,5 @@
  * @hide
  */
 oneway interface ISensorPrivacyListener {
-    void onSensorPrivacyChanged(boolean enabled);
+    void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled);
 }
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index 9564cba..49a1e1e 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -20,23 +20,25 @@
 
 /** @hide */
 interface ISensorPrivacyManager {
-    boolean supportsSensorToggle(int sensor);
+    boolean supportsSensorToggle(int toggleType, int sensor);
 
     void addSensorPrivacyListener(in ISensorPrivacyListener listener);
 
-    void addIndividualSensorPrivacyListener(int userId, int sensor, in ISensorPrivacyListener listener);
+    void addToggleSensorPrivacyListener(in ISensorPrivacyListener listener);
 
     void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
 
-    void removeIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener);
+    void removeToggleSensorPrivacyListener(in ISensorPrivacyListener listener);
 
     boolean isSensorPrivacyEnabled();
 
-    boolean isIndividualSensorPrivacyEnabled(int userId, int sensor);
+    boolean isCombinedToggleSensorPrivacyEnabled(int sensor);
+
+    boolean isToggleSensorPrivacyEnabled(int toggleType, int sensor);
 
     void setSensorPrivacy(boolean enable);
 
-    void setIndividualSensorPrivacy(int userId, int source, int sensor, boolean enable);
+    void setToggleSensorPrivacy(int userId, int source, int sensor, boolean enable);
 
-    void setIndividualSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
+    void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
 }
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index af699d0..fc5fdf7 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -31,22 +31,26 @@
 {
 public:
     enum {
-        INDIVIDUAL_SENSOR_MICROPHONE = 1,
-        INDIVIDUAL_SENSOR_CAMERA = 2
+        TOGGLE_SENSOR_MICROPHONE = 1,
+        TOGGLE_SENSOR_CAMERA = 2
+    };
+
+    enum {
+        TOGGLE_TYPE_SOFTWARE = 1,
+        TOGGLE_TYPE_HARDWARE = 2
     };
 
     SensorPrivacyManager();
 
-    bool supportsSensorToggle(int sensor);
+    bool supportsSensorToggle(int toggleType, int sensor);
     void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
-    status_t addIndividualSensorPrivacyListener(int userId, int sensor,
-            const sp<hardware::ISensorPrivacyListener>& listener);
+    status_t addToggleSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
     void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
-    void removeIndividualSensorPrivacyListener(int sensor,
-            const sp<hardware::ISensorPrivacyListener>& listener);
+    void removeToggleSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
     bool isSensorPrivacyEnabled();
-    bool isIndividualSensorPrivacyEnabled(int userId, int sensor);
-    status_t isIndividualSensorPrivacyEnabled(int userId, int sensor, bool &result);
+    bool isToggleSensorPrivacyEnabled(int sensor);
+    bool isToggleSensorPrivacyEnabled(int toggleType, int sensor);
+    status_t isToggleSensorPrivacyEnabled(int toggleType, int sensor, bool &result);
 
     status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
     status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index f5a22ec..a9380c6 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -235,6 +235,12 @@
         "libui_headers",
     ],
     min_sdk_version: "29",
+    // TODO(b/214400477) to remove use of GraphicBuffer
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+        "test_com.android.media.swcodec",
+    ],
 
     afdo: true,
 }
@@ -258,6 +264,11 @@
         "libmath_headers",
     ],
     min_sdk_version: "29",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+        "test_com.android.media.swcodec",
+    ],
 }
 
 // defaults to enable VALIDATE_REGIONS traces
diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h
index cf08acb..123b275 100644
--- a/libs/ui/include/ui/StretchEffect.h
+++ b/libs/ui/include/ui/StretchEffect.h
@@ -44,6 +44,8 @@
         mappedChildBounds == other.mappedChildBounds;
   }
 
+  bool operator!=(const StretchEffect& other) const { return !(*this == other); }
+
   static bool isZero(float value) {
     constexpr float NON_ZERO_EPSILON = 0.001f;
     return fabsf(value) <= NON_ZERO_EPSILON;
diff --git a/services/gpuservice/gpuwork/Android.bp b/services/gpuservice/gpuwork/Android.bp
index 89b31a6..a9a59bc 100644
--- a/services/gpuservice/gpuwork/Android.bp
+++ b/services/gpuservice/gpuwork/Android.bp
@@ -22,13 +22,13 @@
         "GpuWork.cpp",
     ],
     header_libs: [
+        "bpf_headers",
         "gpu_work_structs",
     ],
     shared_libs: [
         "libbase",
         "libbinder",
         "libbpf_bcc",
-        "libbpf_android",
         "libcutils",
         "liblog",
         "libstatslog",
@@ -43,7 +43,6 @@
     ],
     export_shared_lib_headers: [
         "libbase",
-        "libbpf_android",
         "libstatspull",
     ],
     cppflags: [
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
index e7b1cd4..67ce9f2 100644
--- a/services/gpuservice/gpuwork/GpuWork.cpp
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -24,7 +24,6 @@
 #include <binder/PermissionCache.h>
 #include <bpf/WaitForProgsLoaded.h>
 #include <libbpf.h>
-#include <libbpf_android.h>
 #include <log/log.h>
 #include <random>
 #include <stats_event.h>
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 22a69e5..ed9bfd2 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -22,6 +22,10 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
+inputflinger_tidy_checks = [
+    "android-*",
+]
+
 cc_defaults {
     name: "inputflinger_defaults",
     cpp_std: "c++20",
@@ -38,6 +42,11 @@
     sanitize: {
         misc_undefined: ["bounds"],
     },
+    tidy: true,
+    tidy_checks: [
+        "-*", // Disable all checks not explicitly enabled for now
+    ] + inputflinger_tidy_checks,
+    tidy_checks_as_errors: inputflinger_tidy_checks,
 }
 
 /////////////////////////////////////////////////
@@ -48,7 +57,7 @@
     name: "libinputflinger_sources",
     srcs: [
         "InputClassifier.cpp",
-        "InputClassifierConverter.cpp",
+        "InputCommonConverter.cpp",
         "UnwantedInteractionBlocker.cpp",
         "InputManager.cpp",
     ],
@@ -58,9 +67,10 @@
     name: "libinputflinger_defaults",
     srcs: [":libinputflinger_sources"],
     shared_libs: [
-        "android.hardware.input.classifier@1.0",
+        "android.hardware.input.processor-V1-ndk",
         "libbase",
         "libbinder",
+        "libbinder_ndk",
         "libchrome",
         "libcrypto",
         "libcutils",
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 6c4b11e..3ea0986 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -17,13 +17,15 @@
 #define LOG_TAG "InputClassifier"
 
 #include "InputClassifier.h"
-#include "InputClassifierConverter.h"
+#include "InputCommonConverter.h"
 
-#include <algorithm>
 #include <android-base/stringprintf.h>
-#include <cmath>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <inttypes.h>
 #include <log/log.h>
+#include <algorithm>
+#include <cmath>
 #if defined(__linux__)
     #include <pthread.h>
 #endif
@@ -36,10 +38,9 @@
 #define INDENT5 "          "
 
 using android::base::StringPrintf;
-using android::hardware::hidl_bitfield;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using namespace android::hardware::input;
+using namespace std::chrono_literals;
+using namespace ::aidl::android::hardware::input;
+using aidl::android::hardware::input::processor::IInputProcessor;
 
 namespace android {
 
@@ -55,13 +56,13 @@
     return it->second;
 }
 
-static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
+static MotionClassification getMotionClassification(common::Classification classification) {
     static_assert(MotionClassification::NONE ==
-            static_cast<MotionClassification>(common::V1_0::Classification::NONE));
+                  static_cast<MotionClassification>(common::Classification::NONE));
     static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
-            static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
+                  static_cast<MotionClassification>(common::Classification::AMBIGUOUS_GESTURE));
     static_assert(MotionClassification::DEEP_PRESS ==
-            static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
+                  static_cast<MotionClassification>(common::Classification::DEEP_PRESS));
     return static_cast<MotionClassification>(classification);
 }
 
@@ -70,6 +71,56 @@
             isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN);
 }
 
+static void setCurrentThreadName(const char* name) {
+#if defined(__linux__)
+    // Set the thread name for debugging
+    pthread_setname_np(pthread_self(), name);
+#else
+    (void*)(name); // prevent unused variable warning
+#endif
+}
+
+static std::shared_ptr<IInputProcessor> getService() {
+    const std::string aidl_instance_name = std::string(IInputProcessor::descriptor) + "/default";
+
+    if (!AServiceManager_isDeclared(aidl_instance_name.c_str())) {
+        ALOGI("HAL %s is not declared", aidl_instance_name.c_str());
+        return nullptr;
+    }
+
+    ndk::SpAIBinder binder(AServiceManager_waitForService(aidl_instance_name.c_str()));
+    return IInputProcessor::fromBinder(binder);
+}
+
+// Temporarily releases a held mutex for the lifetime of the instance.
+// Named to match std::scoped_lock
+class scoped_unlock {
+public:
+    explicit scoped_unlock(std::mutex& mutex) : mMutex(mutex) { mMutex.unlock(); }
+    ~scoped_unlock() { mMutex.lock(); }
+
+private:
+    std::mutex& mMutex;
+};
+
+// --- ScopedDeathRecipient ---
+ScopedDeathRecipient::ScopedDeathRecipient(AIBinder_DeathRecipient_onBinderDied onBinderDied,
+                                           void* cookie)
+      : mCookie(cookie) {
+    mRecipient = AIBinder_DeathRecipient_new(onBinderDied);
+}
+
+void ScopedDeathRecipient::linkToDeath(AIBinder* binder) {
+    binder_status_t linked = AIBinder_linkToDeath(binder, mRecipient, mCookie);
+    if (linked != STATUS_OK) {
+        ALOGE("Could not link death recipient to the HAL death");
+    }
+}
+
+ScopedDeathRecipient::~ScopedDeathRecipient() {
+    AIBinder_DeathRecipient_delete(mRecipient);
+}
+
 // --- ClassifierEvent ---
 
 ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
@@ -118,9 +169,8 @@
 
 // --- MotionClassifier ---
 
-MotionClassifier::MotionClassifier(
-        sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
-      : mEvents(MAX_EVENTS), mService(service) {
+MotionClassifier::MotionClassifier(std::shared_ptr<IInputProcessor> service)
+      : mEvents(MAX_EVENTS), mService(std::move(service)) {
     // Under normal operation, we do not need to reset the HAL here. But in the case where system
     // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
     // have received events in the past. That means, that HAL could be in an inconsistent state
@@ -135,23 +185,10 @@
 }
 
 std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
-        sp<android::hardware::hidl_death_recipient> deathRecipient) {
-    sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
-            classifier::V1_0::IInputClassifier::getService();
-    if (!service) {
-        // Not really an error, maybe the device does not have this HAL,
-        // but somehow the feature flag is flipped
-        ALOGI("Could not obtain InputClassifier HAL");
-        return nullptr;
-    }
-
-    const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
-    if (!linked) {
-        ALOGE("Could not link death recipient to the HAL death");
-        return nullptr;
-    }
+        std::shared_ptr<IInputProcessor> service) {
+    LOG_ALWAYS_FATAL_IF(service == nullptr);
     // Using 'new' to access a non-public constructor
-    return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
+    return std::unique_ptr<MotionClassifier>(new MotionClassifier(std::move(service)));
 }
 
 MotionClassifier::~MotionClassifier() {
@@ -176,14 +213,12 @@
         switch (event.type) {
             case ClassifierEventType::MOTION: {
                 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
-                common::V1_0::MotionEvent motionEvent =
-                        notifyMotionArgsToHalMotionEvent(*motionArgs);
-                Return<common::V1_0::Classification> response = mService->classify(motionEvent);
-                halResponseOk = response.isOk();
-                if (halResponseOk) {
-                    common::V1_0::Classification halClassification = response;
+                common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(*motionArgs);
+                common::Classification classification;
+                ndk::ScopedAStatus response = mService->classify(motionEvent, &classification);
+                if (response.isOk()) {
                     updateClassification(motionArgs->deviceId, motionArgs->eventTime,
-                            getMotionClassification(halClassification));
+                                         getMotionClassification(classification));
                 }
                 break;
             }
@@ -300,7 +335,8 @@
     if (!mService) {
         return "null";
     }
-    if (mService->ping().isOk()) {
+
+    if (AIBinder_ping(mService->asBinder().get()) == STATUS_OK) {
         return "running";
     }
     return "not responding";
@@ -329,40 +365,53 @@
     }
 }
 
-// --- HalDeathRecipient
-
-InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
-
-void InputClassifier::HalDeathRecipient::serviceDied(
-        uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
-    sp<android::hidl::base::V1_0::IBase> service = who.promote();
-    if (service) {
-        service->unlinkToDeath(this);
-    }
-    mParent.setMotionClassifier(nullptr);
-}
-
 // --- InputClassifier ---
 
-InputClassifier::InputClassifier(InputListenerInterface& listener)
-      : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
+InputClassifier::InputClassifier(InputListenerInterface& listener) : mListener(listener) {}
+
+void InputClassifier::onBinderDied(void* cookie) {
+    InputClassifier* classifier = static_cast<InputClassifier*>(cookie);
+    if (classifier == nullptr) {
+        LOG_ALWAYS_FATAL("Cookie is not valid");
+        return;
+    }
+    classifier->setMotionClassifierEnabled(false);
+}
 
 void InputClassifier::setMotionClassifierEnabled(bool enabled) {
+    std::scoped_lock lock(mLock);
     if (enabled) {
         ALOGI("Enabling motion classifier");
-        if (mInitializeMotionClassifierThread.joinable()) {
-            mInitializeMotionClassifierThread.join();
+        if (mInitializeMotionClassifier.valid()) {
+            scoped_unlock unlock(mLock);
+            std::future_status status = mInitializeMotionClassifier.wait_for(5s);
+            if (status != std::future_status::ready) {
+                /**
+                 * We don't have a better option here than to crash. We can't stop the thread,
+                 * and we can't continue because 'mInitializeMotionClassifier' will block in its
+                 * destructor.
+                 */
+                LOG_ALWAYS_FATAL("The thread to load IInputClassifier is stuck!");
+            }
         }
-        mInitializeMotionClassifierThread = std::thread(
-                [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
-#if defined(__linux__)
-        // Set the thread name for debugging
-        pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
-                           "Create MotionClassifier");
-#endif
+        mInitializeMotionClassifier = std::async(std::launch::async, [this] {
+            setCurrentThreadName("Create MotionClassifier");
+            std::shared_ptr<IInputProcessor> service = getService();
+            if (service == nullptr) {
+                // Keep the MotionClassifier null, no service was found
+                return;
+            }
+            { // acquire lock
+                std::scoped_lock threadLock(mLock);
+                mHalDeathRecipient =
+                        std::make_unique<ScopedDeathRecipient>(onBinderDied, this /*cookie*/);
+                mHalDeathRecipient->linkToDeath(service->asBinder().get());
+                setMotionClassifierLocked(MotionClassifier::create(std::move(service)));
+            } // release lock
+        });
     } else {
         ALOGI("Disabling motion classifier");
-        setMotionClassifier(nullptr);
+        setMotionClassifierLocked(nullptr);
     }
 }
 
@@ -419,9 +468,13 @@
     mListener.notifyPointerCaptureChanged(args);
 }
 
-void InputClassifier::setMotionClassifier(
-        std::unique_ptr<MotionClassifierInterface> motionClassifier) {
-    std::scoped_lock lock(mLock);
+void InputClassifier::setMotionClassifierLocked(
+        std::unique_ptr<MotionClassifierInterface> motionClassifier) REQUIRES(mLock) {
+    if (motionClassifier == nullptr) {
+        // Destroy the ScopedDeathRecipient object, which will cause it to unlinkToDeath.
+        // We can't call 'unlink' here because we don't have the binder handle.
+        mHalDeathRecipient = nullptr;
+    }
     mMotionClassifier = std::move(motionClassifier);
 }
 
@@ -438,9 +491,6 @@
 }
 
 InputClassifier::~InputClassifier() {
-    if (mInitializeMotionClassifierThread.joinable()) {
-        mInitializeMotionClassifierThread.join();
-    }
 }
 
 } // namespace android
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index deeae7c..e2a0bc2 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -18,13 +18,13 @@
 #define _UI_INPUT_CLASSIFIER_H
 
 #include <android-base/thread_annotations.h>
+#include <future>
 #include <thread>
 #include <unordered_map>
 
+#include <aidl/android/hardware/input/processor/IInputProcessor.h>
 #include "BlockingQueue.h"
 #include "InputListener.h"
-#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
-
 namespace android {
 
 enum class ClassifierEventType : uint8_t {
@@ -102,6 +102,19 @@
 
 // --- Implementations ---
 
+class ScopedDeathRecipient {
+public:
+    explicit ScopedDeathRecipient(AIBinder_DeathRecipient_onBinderDied onBinderDied, void* cookie);
+    ScopedDeathRecipient(const ScopedDeathRecipient&) = delete;
+    ScopedDeathRecipient& operator=(ScopedDeathRecipient const&) = delete;
+    void linkToDeath(AIBinder* binder);
+    ~ScopedDeathRecipient();
+
+private:
+    AIBinder_DeathRecipient* mRecipient;
+    void* mCookie;
+};
+
 /**
  * Implementation of MotionClassifierInterface that calls the InputClassifier HAL
  * in order to determine the classification for the current gesture.
@@ -121,7 +134,7 @@
      * This function should be called asynchronously, because getService takes a long time.
      */
     static std::unique_ptr<MotionClassifierInterface> create(
-            sp<android::hardware::hidl_death_recipient> deathRecipient);
+            std::shared_ptr<aidl::android::hardware::input::processor::IInputProcessor> service);
 
     ~MotionClassifier();
 
@@ -143,7 +156,7 @@
 private:
     friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
     explicit MotionClassifier(
-            sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
+            std::shared_ptr<aidl::android::hardware::input::processor::IInputProcessor> service);
 
     // The events that need to be sent to the HAL.
     BlockingQueue<ClassifierEvent> mEvents;
@@ -162,14 +175,14 @@
      */
     void processEvents();
     /**
-     * Access to the InputClassifier HAL. May be null if init() hasn't completed yet.
+     * Access to the InputProcessor HAL. May be null if init() hasn't completed yet.
      * When init() successfully completes, mService is guaranteed to remain non-null and to not
      * change its value until MotionClassifier is destroyed.
      * This variable is *not* guarded by mLock in the InputClassifier thread, because
      * that thread knows exactly when this variable is initialized.
      * When accessed in any other thread, mService is checked for nullness with a lock.
      */
-    sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService;
+    std::shared_ptr<aidl::android::hardware::input::processor::IInputProcessor> mService;
     std::mutex mLock;
     /**
      * Per-device input classifications. Should only be accessed using the
@@ -224,21 +237,21 @@
 public:
     explicit InputClassifier(InputListenerInterface& listener);
 
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
-    virtual void notifyKey(const NotifyKeyArgs* args) override;
-    virtual void notifyMotion(const NotifyMotionArgs* args) override;
-    virtual void notifySwitch(const NotifySwitchArgs* args) override;
-    virtual void notifySensor(const NotifySensorArgs* args) override;
-    virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+    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;
 
-    virtual void dump(std::string& dump) override;
+    void dump(std::string& dump) override;
 
     ~InputClassifier();
 
     // Called from InputManager
-    virtual void setMotionClassifierEnabled(bool enabled) override;
+    void setMotionClassifierEnabled(bool enabled) override;
 
 private:
     // Protect access to mMotionClassifier, since it may become null via a hidl callback
@@ -247,7 +260,8 @@
     InputListenerInterface& mListener;
 
     std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
-    std::thread mInitializeMotionClassifierThread;
+    std::future<void> mInitializeMotionClassifier GUARDED_BY(mLock);
+
     /**
      * Set the value of mMotionClassifier.
      * This is called from 2 different threads:
@@ -255,25 +269,12 @@
      * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause
      *    mMotionClassifier to become nullptr.
      */
-    void setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier);
+    void setMotionClassifierLocked(std::unique_ptr<MotionClassifierInterface> motionClassifier)
+            REQUIRES(mLock);
 
-    /**
-     * The deathRecipient will call setMotionClassifier(null) when the HAL dies.
-     */
-    class HalDeathRecipient : public android::hardware::hidl_death_recipient {
-    public:
-        explicit HalDeathRecipient(InputClassifier& parent);
-        virtual void serviceDied(uint64_t cookie,
-                                 const wp<android::hidl::base::V1_0::IBase>& who) override;
+    static void onBinderDied(void* cookie);
 
-    private:
-        InputClassifier& mParent;
-    };
-    // We retain a reference to death recipient, because the death recipient will be calling
-    // ~MotionClassifier if the HAL dies.
-    // If we don't retain a reference, and MotionClassifier is the only owner of the death
-    // recipient, the serviceDied call will cause death recipient to call its own destructor.
-    sp<HalDeathRecipient> mHalDeathRecipient;
+    std::unique_ptr<ScopedDeathRecipient> mHalDeathRecipient GUARDED_BY(mLock);
 };
 
 } // namespace android
diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp
deleted file mode 100644
index b58a188..0000000
--- a/services/inputflinger/InputClassifierConverter.cpp
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * 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.
- */
-
-#include "InputClassifierConverter.h"
-
-using android::hardware::hidl_bitfield;
-using namespace android::hardware::input;
-
-namespace android {
-
-static common::V1_0::Source getSource(uint32_t source) {
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_UNKNOWN) ==
-            common::V1_0::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_KEYBOARD) ==
-            common::V1_0::Source::KEYBOARD, "SOURCE_KEYBOARD mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_DPAD) ==
-            common::V1_0::Source::DPAD, "SOURCE_DPAD mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_GAMEPAD) ==
-            common::V1_0::Source::GAMEPAD, "SOURCE_GAMEPAD mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHSCREEN) ==
-            common::V1_0::Source::TOUCHSCREEN, "SOURCE_TOUCHSCREEN mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE) ==
-            common::V1_0::Source::MOUSE, "SOURCE_MOUSE mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_STYLUS) ==
-            common::V1_0::Source::STYLUS, "SOURCE_STYLUS mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_BLUETOOTH_STYLUS) ==
-            common::V1_0::Source::BLUETOOTH_STYLUS, "SOURCE_BLUETOOTH_STYLUS mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TRACKBALL) ==
-            common::V1_0::Source::TRACKBALL, "SOURCE_TRACKBALL mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_MOUSE_RELATIVE) ==
-            common::V1_0::Source::MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCHPAD) ==
-            common::V1_0::Source::TOUCHPAD, "SOURCE_TOUCHPAD mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_TOUCH_NAVIGATION) ==
-            common::V1_0::Source::TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_JOYSTICK) ==
-            common::V1_0::Source::JOYSTICK, "SOURCE_JOYSTICK mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ROTARY_ENCODER) ==
-            common::V1_0::Source::ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER mismatch");
-    static_assert(static_cast<common::V1_0::Source>(AINPUT_SOURCE_ANY) ==
-            common::V1_0::Source::ANY, "SOURCE_ANY mismatch");
-    return static_cast<common::V1_0::Source>(source);
-}
-
-static common::V1_0::Action getAction(int32_t actionMasked) {
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_DOWN) ==
-            common::V1_0::Action::DOWN, "ACTION_DOWN mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_UP) ==
-            common::V1_0::Action::UP, "ACTION_UP mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_MOVE) ==
-            common::V1_0::Action::MOVE, "ACTION_MOVE mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_CANCEL) ==
-            common::V1_0::Action::CANCEL, "ACTION_CANCEL mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_OUTSIDE) ==
-            common::V1_0::Action::OUTSIDE, "ACTION_OUTSIDE mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_DOWN) ==
-            common::V1_0::Action::POINTER_DOWN, "ACTION_POINTER_DOWN mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_POINTER_UP) ==
-            common::V1_0::Action::POINTER_UP, "ACTION_POINTER_UP mismatch");
-    static_assert(static_cast<common::V1_0::Action>( AMOTION_EVENT_ACTION_HOVER_MOVE) ==
-            common::V1_0::Action::HOVER_MOVE, "ACTION_HOVER_MOVE mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_SCROLL) ==
-            common::V1_0::Action::SCROLL, "ACTION_SCROLL mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_ENTER) ==
-            common::V1_0::Action::HOVER_ENTER, "ACTION_HOVER_ENTER mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_HOVER_EXIT) ==
-            common::V1_0::Action::HOVER_EXIT, "ACTION_HOVER_EXIT mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_PRESS) ==
-            common::V1_0::Action::BUTTON_PRESS, "ACTION_BUTTON_PRESS mismatch");
-    static_assert(static_cast<common::V1_0::Action>(AMOTION_EVENT_ACTION_BUTTON_RELEASE) ==
-            common::V1_0::Action::BUTTON_RELEASE, "ACTION_BUTTON_RELEASE mismatch");
-    return static_cast<common::V1_0::Action>(actionMasked);
-}
-
-static common::V1_0::Button getActionButton(int32_t actionButton) {
-    static_assert(static_cast<common::V1_0::Button>(0) ==
-            common::V1_0::Button::NONE, "BUTTON_NONE mismatch");
-    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_PRIMARY) ==
-            common::V1_0::Button::PRIMARY, "BUTTON_PRIMARY mismatch");
-    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_SECONDARY) ==
-            common::V1_0::Button::SECONDARY, "BUTTON_SECONDARY mismatch");
-    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_TERTIARY) ==
-            common::V1_0::Button::TERTIARY, "BUTTON_TERTIARY mismatch");
-    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_BACK) ==
-            common::V1_0::Button::BACK, "BUTTON_BACK mismatch");
-    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_FORWARD) ==
-            common::V1_0::Button::FORWARD, "BUTTON_FORWARD mismatch");
-    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) ==
-            common::V1_0::Button::STYLUS_PRIMARY, "BUTTON_STYLUS_PRIMARY mismatch");
-    static_assert(static_cast<common::V1_0::Button>(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) ==
-            common::V1_0::Button::STYLUS_SECONDARY, "BUTTON_STYLUS_SECONDARY mismatch");
-    return static_cast<common::V1_0::Button>(actionButton);
-}
-
-static hidl_bitfield<common::V1_0::Flag> getFlags(int32_t flags) {
-    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) ==
-            common::V1_0::Flag::WINDOW_IS_OBSCURED);
-    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) ==
-            common::V1_0::Flag::IS_GENERATED_GESTURE);
-    static_assert(static_cast<common::V1_0::Flag>(AMOTION_EVENT_FLAG_TAINTED) ==
-            common::V1_0::Flag::TAINTED);
-    return static_cast<hidl_bitfield<common::V1_0::Flag>>(flags);
-}
-
-static hidl_bitfield<common::V1_0::PolicyFlag> getPolicyFlags(int32_t flags) {
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_WAKE) ==
-            common::V1_0::PolicyFlag::WAKE);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_VIRTUAL) ==
-            common::V1_0::PolicyFlag::VIRTUAL);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FUNCTION) ==
-            common::V1_0::PolicyFlag::FUNCTION);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_GESTURE) ==
-            common::V1_0::PolicyFlag::GESTURE);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INJECTED) ==
-            common::V1_0::PolicyFlag::INJECTED);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_TRUSTED) ==
-            common::V1_0::PolicyFlag::TRUSTED);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_FILTERED) ==
-            common::V1_0::PolicyFlag::FILTERED);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_DISABLE_KEY_REPEAT) ==
-            common::V1_0::PolicyFlag::DISABLE_KEY_REPEAT);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_INTERACTIVE) ==
-            common::V1_0::PolicyFlag::INTERACTIVE);
-    static_assert(static_cast<common::V1_0::PolicyFlag>(POLICY_FLAG_PASS_TO_USER) ==
-            common::V1_0::PolicyFlag::PASS_TO_USER);
-    return static_cast<hidl_bitfield<common::V1_0::PolicyFlag>>(flags);
-}
-
-static hidl_bitfield<common::V1_0::EdgeFlag> getEdgeFlags(int32_t flags) {
-    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_NONE) ==
-            common::V1_0::EdgeFlag::NONE);
-    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_TOP) ==
-            common::V1_0::EdgeFlag::TOP);
-    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_BOTTOM) ==
-            common::V1_0::EdgeFlag::BOTTOM);
-    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_LEFT) ==
-            common::V1_0::EdgeFlag::LEFT);
-    static_assert(static_cast<common::V1_0::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_RIGHT) ==
-            common::V1_0::EdgeFlag::RIGHT);
-    return static_cast<hidl_bitfield<common::V1_0::EdgeFlag>>(flags);
-}
-
-static hidl_bitfield<common::V1_0::Meta> getMetastate(int32_t state) {
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_NONE) ==
-            common::V1_0::Meta::NONE);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_ON) ==
-            common::V1_0::Meta::ALT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_LEFT_ON) ==
-            common::V1_0::Meta::ALT_LEFT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_ALT_RIGHT_ON) ==
-            common::V1_0::Meta::ALT_RIGHT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_ON) ==
-            common::V1_0::Meta::SHIFT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_LEFT_ON) ==
-            common::V1_0::Meta::SHIFT_LEFT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_SHIFT_RIGHT_ON) ==
-            common::V1_0::Meta::SHIFT_RIGHT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_SYM_ON) ==
-            common::V1_0::Meta::SYM_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_FUNCTION_ON) ==
-            common::V1_0::Meta::FUNCTION_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_ON) ==
-            common::V1_0::Meta::CTRL_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_LEFT_ON) ==
-            common::V1_0::Meta::CTRL_LEFT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_CTRL_RIGHT_ON) ==
-            common::V1_0::Meta::CTRL_RIGHT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_ON) ==
-            common::V1_0::Meta::META_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_LEFT_ON) ==
-            common::V1_0::Meta::META_LEFT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_META_RIGHT_ON) ==
-            common::V1_0::Meta::META_RIGHT_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_CAPS_LOCK_ON) ==
-            common::V1_0::Meta::CAPS_LOCK_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_NUM_LOCK_ON) ==
-            common::V1_0::Meta::NUM_LOCK_ON);
-    static_assert(static_cast<common::V1_0::Meta>(AMETA_SCROLL_LOCK_ON) ==
-            common::V1_0::Meta::SCROLL_LOCK_ON);
-    return static_cast<hidl_bitfield<common::V1_0::Meta>>(state);
-}
-
-static hidl_bitfield<common::V1_0::Button> getButtonState(int32_t buttonState) {
-    // No need for static_assert here.
-    // The button values have already been asserted in getActionButton(..) above
-    return static_cast<hidl_bitfield<common::V1_0::Button>>(buttonState);
-}
-
-static common::V1_0::ToolType getToolType(int32_t toolType) {
-    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) ==
-            common::V1_0::ToolType::UNKNOWN);
-    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_FINGER) ==
-            common::V1_0::ToolType::FINGER);
-    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_STYLUS) ==
-            common::V1_0::ToolType::STYLUS);
-    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_MOUSE) ==
-            common::V1_0::ToolType::MOUSE);
-    static_assert(static_cast<common::V1_0::ToolType>(AMOTION_EVENT_TOOL_TYPE_ERASER) ==
-            common::V1_0::ToolType::ERASER);
-    return static_cast<common::V1_0::ToolType>(toolType);
-}
-
-// MotionEvent axes asserts
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_X) ==
-        common::V1_0::Axis::X);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Y) ==
-        common::V1_0::Axis::Y);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_PRESSURE) ==
-        common::V1_0::Axis::PRESSURE);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SIZE) ==
-        common::V1_0::Axis::SIZE);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MAJOR) ==
-        common::V1_0::Axis::TOUCH_MAJOR);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOUCH_MINOR) ==
-        common::V1_0::Axis::TOUCH_MINOR);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MAJOR) ==
-        common::V1_0::Axis::TOOL_MAJOR);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TOOL_MINOR) ==
-        common::V1_0::Axis::TOOL_MINOR);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_ORIENTATION) ==
-        common::V1_0::Axis::ORIENTATION);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_VSCROLL) ==
-        common::V1_0::Axis::VSCROLL);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HSCROLL) ==
-        common::V1_0::Axis::HSCROLL);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_Z) ==
-        common::V1_0::Axis::Z);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RX) ==
-        common::V1_0::Axis::RX);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RY) ==
-        common::V1_0::Axis::RY);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RZ) ==
-        common::V1_0::Axis::RZ);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_X) ==
-        common::V1_0::Axis::HAT_X);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_HAT_Y) ==
-        common::V1_0::Axis::HAT_Y);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_LTRIGGER) ==
-        common::V1_0::Axis::LTRIGGER);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RTRIGGER) ==
-        common::V1_0::Axis::RTRIGGER);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_THROTTLE) ==
-        common::V1_0::Axis::THROTTLE);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RUDDER) ==
-        common::V1_0::Axis::RUDDER);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_WHEEL) ==
-        common::V1_0::Axis::WHEEL);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GAS) ==
-        common::V1_0::Axis::GAS);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_BRAKE) ==
-        common::V1_0::Axis::BRAKE);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_DISTANCE) ==
-        common::V1_0::Axis::DISTANCE);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_TILT) ==
-        common::V1_0::Axis::TILT);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_SCROLL) ==
-        common::V1_0::Axis::SCROLL);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_X) ==
-        common::V1_0::Axis::RELATIVE_X);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_RELATIVE_Y) ==
-        common::V1_0::Axis::RELATIVE_Y);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_1) ==
-        common::V1_0::Axis::GENERIC_1);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_2) ==
-        common::V1_0::Axis::GENERIC_2);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_3) ==
-        common::V1_0::Axis::GENERIC_3);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_4) ==
-        common::V1_0::Axis::GENERIC_4);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_5) ==
-        common::V1_0::Axis::GENERIC_5);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_6) ==
-        common::V1_0::Axis::GENERIC_6);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_7) ==
-        common::V1_0::Axis::GENERIC_7);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_8) ==
-        common::V1_0::Axis::GENERIC_8);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_9) ==
-        common::V1_0::Axis::GENERIC_9);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_10) ==
-        common::V1_0::Axis::GENERIC_10);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_11) ==
-        common::V1_0::Axis::GENERIC_11);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_12) ==
-        common::V1_0::Axis::GENERIC_12);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) ==
-        common::V1_0::Axis::GENERIC_13);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) ==
-        common::V1_0::Axis::GENERIC_14);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) ==
-        common::V1_0::Axis::GENERIC_15);
-static_assert(static_cast<common::V1_0::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) ==
-        common::V1_0::Axis::GENERIC_16);
-
-static common::V1_0::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
-    common::V1_0::VideoFrame out;
-    out.width = frame.getWidth();
-    out.height = frame.getHeight();
-    out.data = frame.getData();
-    struct timeval timestamp = frame.getTimestamp();
-    out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) +
-             microseconds_to_nanoseconds(timestamp.tv_usec);
-    return out;
-}
-
-static std::vector<common::V1_0::VideoFrame> convertVideoFrames(
-        const std::vector<TouchVideoFrame>& frames) {
-    std::vector<common::V1_0::VideoFrame> out;
-    for (const TouchVideoFrame& frame : frames) {
-        out.push_back(getHalVideoFrame(frame));
-    }
-    return out;
-}
-
-static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args,
-        std::vector<common::V1_0::PointerProperties>* outPointerProperties,
-        std::vector<common::V1_0::PointerCoords>* outPointerCoords) {
-    outPointerProperties->reserve(args.pointerCount);
-    outPointerCoords->reserve(args.pointerCount);
-    for (size_t i = 0; i < args.pointerCount; i++) {
-        common::V1_0::PointerProperties properties;
-        properties.id = args.pointerProperties[i].id;
-        properties.toolType = getToolType(args.pointerProperties[i].toolType);
-        outPointerProperties->push_back(properties);
-
-        common::V1_0::PointerCoords coords;
-        // OK to copy bits because we have static_assert for pointerCoords axes
-        coords.bits = args.pointerCoords[i].bits;
-        coords.values = std::vector<float>(
-                args.pointerCoords[i].values,
-                args.pointerCoords[i].values + BitSet64::count(args.pointerCoords[i].bits));
-        outPointerCoords->push_back(coords);
-    }
-}
-
-common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArgs& args) {
-    common::V1_0::MotionEvent event;
-    event.deviceId = args.deviceId;
-    event.source = getSource(args.source);
-    event.displayId = args.displayId;
-    event.downTime = args.downTime;
-    event.eventTime = args.eventTime;
-    event.deviceTimestamp = 0;
-    event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK);
-    event.actionIndex = MotionEvent::getActionIndex(args.action);
-    event.actionButton = getActionButton(args.actionButton);
-    event.flags = getFlags(args.flags);
-    event.policyFlags = getPolicyFlags(args.policyFlags);
-    event.edgeFlags = getEdgeFlags(args.edgeFlags);
-    event.metaState = getMetastate(args.metaState);
-    event.buttonState = getButtonState(args.buttonState);
-    event.xPrecision = args.xPrecision;
-    event.yPrecision = args.yPrecision;
-
-    std::vector<common::V1_0::PointerProperties> pointerProperties;
-    std::vector<common::V1_0::PointerCoords> pointerCoords;
-    getHidlPropertiesAndCoords(args, /*out*/&pointerProperties, /*out*/&pointerCoords);
-    event.pointerProperties = pointerProperties;
-    event.pointerCoords = pointerCoords;
-
-    event.frames = convertVideoFrames(args.videoFrames);
-
-    return event;
-}
-
-} // namespace android
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
new file mode 100644
index 0000000..8aee39f
--- /dev/null
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -0,0 +1,339 @@
+/*
+ * 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 "InputCommonConverter.h"
+
+using namespace ::aidl::android::hardware::input;
+
+namespace android {
+
+static common::Source getSource(uint32_t source) {
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_UNKNOWN) == common::Source::UNKNOWN,
+                  "SOURCE_UNKNOWN mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_KEYBOARD) == common::Source::KEYBOARD,
+                  "SOURCE_KEYBOARD mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_DPAD) == common::Source::DPAD,
+                  "SOURCE_DPAD mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_GAMEPAD) == common::Source::GAMEPAD,
+                  "SOURCE_GAMEPAD mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_TOUCHSCREEN) ==
+                          common::Source::TOUCHSCREEN,
+                  "SOURCE_TOUCHSCREEN mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_MOUSE) == common::Source::MOUSE,
+                  "SOURCE_MOUSE mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_STYLUS) == common::Source::STYLUS,
+                  "SOURCE_STYLUS mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_BLUETOOTH_STYLUS) ==
+                          common::Source::BLUETOOTH_STYLUS,
+                  "SOURCE_BLUETOOTH_STYLUS mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_TRACKBALL) == common::Source::TRACKBALL,
+                  "SOURCE_TRACKBALL mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_MOUSE_RELATIVE) ==
+                          common::Source::MOUSE_RELATIVE,
+                  "SOURCE_MOUSE_RELATIVE mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_TOUCHPAD) == common::Source::TOUCHPAD,
+                  "SOURCE_TOUCHPAD mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_TOUCH_NAVIGATION) ==
+                          common::Source::TOUCH_NAVIGATION,
+                  "SOURCE_TOUCH_NAVIGATION mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_JOYSTICK) == common::Source::JOYSTICK,
+                  "SOURCE_JOYSTICK mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_ROTARY_ENCODER) ==
+                          common::Source::ROTARY_ENCODER,
+                  "SOURCE_ROTARY_ENCODER mismatch");
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_HDMI) == common::Source::HDMI);
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_SENSOR) == common::Source::SENSOR);
+    static_assert(static_cast<common::Source>(AINPUT_SOURCE_ANY) == common::Source::ANY,
+                  "SOURCE_ANY mismatch");
+    return static_cast<common::Source>(source);
+}
+
+static common::Action getAction(int32_t actionMasked) {
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_DOWN) == common::Action::DOWN,
+                  "ACTION_DOWN mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_UP) == common::Action::UP,
+                  "ACTION_UP mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_MOVE) == common::Action::MOVE,
+                  "ACTION_MOVE mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_CANCEL) ==
+                          common::Action::CANCEL,
+                  "ACTION_CANCEL mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_OUTSIDE) ==
+                          common::Action::OUTSIDE,
+                  "ACTION_OUTSIDE mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_POINTER_DOWN) ==
+                          common::Action::POINTER_DOWN,
+                  "ACTION_POINTER_DOWN mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_POINTER_UP) ==
+                          common::Action::POINTER_UP,
+                  "ACTION_POINTER_UP mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_HOVER_MOVE) ==
+                          common::Action::HOVER_MOVE,
+                  "ACTION_HOVER_MOVE mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_SCROLL) ==
+                          common::Action::SCROLL,
+                  "ACTION_SCROLL mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_HOVER_ENTER) ==
+                          common::Action::HOVER_ENTER,
+                  "ACTION_HOVER_ENTER mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_HOVER_EXIT) ==
+                          common::Action::HOVER_EXIT,
+                  "ACTION_HOVER_EXIT mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_BUTTON_PRESS) ==
+                          common::Action::BUTTON_PRESS,
+                  "ACTION_BUTTON_PRESS mismatch");
+    static_assert(static_cast<common::Action>(AMOTION_EVENT_ACTION_BUTTON_RELEASE) ==
+                          common::Action::BUTTON_RELEASE,
+                  "ACTION_BUTTON_RELEASE mismatch");
+    return static_cast<common::Action>(actionMasked);
+}
+
+static common::Button getActionButton(int32_t actionButton) {
+    static_assert(static_cast<common::Button>(0) == common::Button::NONE, "BUTTON_NONE mismatch");
+    static_assert(static_cast<common::Button>(AMOTION_EVENT_BUTTON_PRIMARY) ==
+                          common::Button::PRIMARY,
+                  "BUTTON_PRIMARY mismatch");
+    static_assert(static_cast<common::Button>(AMOTION_EVENT_BUTTON_SECONDARY) ==
+                          common::Button::SECONDARY,
+                  "BUTTON_SECONDARY mismatch");
+    static_assert(static_cast<common::Button>(AMOTION_EVENT_BUTTON_TERTIARY) ==
+                          common::Button::TERTIARY,
+                  "BUTTON_TERTIARY mismatch");
+    static_assert(static_cast<common::Button>(AMOTION_EVENT_BUTTON_BACK) == common::Button::BACK,
+                  "BUTTON_BACK mismatch");
+    static_assert(static_cast<common::Button>(AMOTION_EVENT_BUTTON_FORWARD) ==
+                          common::Button::FORWARD,
+                  "BUTTON_FORWARD mismatch");
+    static_assert(static_cast<common::Button>(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) ==
+                          common::Button::STYLUS_PRIMARY,
+                  "BUTTON_STYLUS_PRIMARY mismatch");
+    static_assert(static_cast<common::Button>(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) ==
+                          common::Button::STYLUS_SECONDARY,
+                  "BUTTON_STYLUS_SECONDARY mismatch");
+    return static_cast<common::Button>(actionButton);
+}
+
+static common::Flag getFlags(int32_t flags) {
+    static_assert(static_cast<common::Flag>(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) ==
+                  common::Flag::WINDOW_IS_OBSCURED);
+    static_assert(static_cast<common::Flag>(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) ==
+                  common::Flag::IS_GENERATED_GESTURE);
+    static_assert(static_cast<common::Flag>(AMOTION_EVENT_FLAG_TAINTED) == common::Flag::TAINTED);
+    return static_cast<common::Flag>(flags);
+}
+
+static common::PolicyFlag getPolicyFlags(int32_t flags) {
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_WAKE) == common::PolicyFlag::WAKE);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_VIRTUAL) ==
+                  common::PolicyFlag::VIRTUAL);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_FUNCTION) ==
+                  common::PolicyFlag::FUNCTION);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_GESTURE) ==
+                  common::PolicyFlag::GESTURE);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_INJECTED) ==
+                  common::PolicyFlag::INJECTED);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_TRUSTED) ==
+                  common::PolicyFlag::TRUSTED);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_FILTERED) ==
+                  common::PolicyFlag::FILTERED);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_DISABLE_KEY_REPEAT) ==
+                  common::PolicyFlag::DISABLE_KEY_REPEAT);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_INTERACTIVE) ==
+                  common::PolicyFlag::INTERACTIVE);
+    static_assert(static_cast<common::PolicyFlag>(POLICY_FLAG_PASS_TO_USER) ==
+                  common::PolicyFlag::PASS_TO_USER);
+    return static_cast<common::PolicyFlag>(flags);
+}
+
+static common::EdgeFlag getEdgeFlags(int32_t flags) {
+    static_assert(static_cast<common::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_NONE) ==
+                  common::EdgeFlag::NONE);
+    static_assert(static_cast<common::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_TOP) ==
+                  common::EdgeFlag::TOP);
+    static_assert(static_cast<common::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_BOTTOM) ==
+                  common::EdgeFlag::BOTTOM);
+    static_assert(static_cast<common::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_LEFT) ==
+                  common::EdgeFlag::LEFT);
+    static_assert(static_cast<common::EdgeFlag>(AMOTION_EVENT_EDGE_FLAG_RIGHT) ==
+                  common::EdgeFlag::RIGHT);
+    return static_cast<common::EdgeFlag>(flags);
+}
+
+static common::Meta getMetastate(int32_t state) {
+    static_assert(static_cast<common::Meta>(AMETA_NONE) == common::Meta::NONE);
+    static_assert(static_cast<common::Meta>(AMETA_ALT_ON) == common::Meta::ALT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_ALT_LEFT_ON) == common::Meta::ALT_LEFT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_ALT_RIGHT_ON) == common::Meta::ALT_RIGHT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_SHIFT_ON) == common::Meta::SHIFT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_SHIFT_LEFT_ON) == common::Meta::SHIFT_LEFT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_SHIFT_RIGHT_ON) == common::Meta::SHIFT_RIGHT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_SYM_ON) == common::Meta::SYM_ON);
+    static_assert(static_cast<common::Meta>(AMETA_FUNCTION_ON) == common::Meta::FUNCTION_ON);
+    static_assert(static_cast<common::Meta>(AMETA_CTRL_ON) == common::Meta::CTRL_ON);
+    static_assert(static_cast<common::Meta>(AMETA_CTRL_LEFT_ON) == common::Meta::CTRL_LEFT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_CTRL_RIGHT_ON) == common::Meta::CTRL_RIGHT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_META_ON) == common::Meta::META_ON);
+    static_assert(static_cast<common::Meta>(AMETA_META_LEFT_ON) == common::Meta::META_LEFT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_META_RIGHT_ON) == common::Meta::META_RIGHT_ON);
+    static_assert(static_cast<common::Meta>(AMETA_CAPS_LOCK_ON) == common::Meta::CAPS_LOCK_ON);
+    static_assert(static_cast<common::Meta>(AMETA_NUM_LOCK_ON) == common::Meta::NUM_LOCK_ON);
+    static_assert(static_cast<common::Meta>(AMETA_SCROLL_LOCK_ON) == common::Meta::SCROLL_LOCK_ON);
+    return static_cast<common::Meta>(state);
+}
+
+static common::Button getButtonState(int32_t buttonState) {
+    // No need for static_assert here.
+    // The button values have already been asserted in getActionButton(..) above
+    return static_cast<common::Button>(buttonState);
+}
+
+static common::ToolType getToolType(int32_t toolType) {
+    static_assert(static_cast<common::ToolType>(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) ==
+                  common::ToolType::UNKNOWN);
+    static_assert(static_cast<common::ToolType>(AMOTION_EVENT_TOOL_TYPE_FINGER) ==
+                  common::ToolType::FINGER);
+    static_assert(static_cast<common::ToolType>(AMOTION_EVENT_TOOL_TYPE_STYLUS) ==
+                  common::ToolType::STYLUS);
+    static_assert(static_cast<common::ToolType>(AMOTION_EVENT_TOOL_TYPE_MOUSE) ==
+                  common::ToolType::MOUSE);
+    static_assert(static_cast<common::ToolType>(AMOTION_EVENT_TOOL_TYPE_ERASER) ==
+                  common::ToolType::ERASER);
+    return static_cast<common::ToolType>(toolType);
+}
+
+// MotionEvent axes asserts
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_X) == common::Axis::X);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_Y) == common::Axis::Y);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_PRESSURE) == common::Axis::PRESSURE);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_SIZE) == common::Axis::SIZE);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_TOUCH_MAJOR) ==
+              common::Axis::TOUCH_MAJOR);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_TOUCH_MINOR) ==
+              common::Axis::TOUCH_MINOR);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_TOOL_MAJOR) == common::Axis::TOOL_MAJOR);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_TOOL_MINOR) == common::Axis::TOOL_MINOR);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_ORIENTATION) ==
+              common::Axis::ORIENTATION);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_VSCROLL) == common::Axis::VSCROLL);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_HSCROLL) == common::Axis::HSCROLL);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_Z) == common::Axis::Z);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_RX) == common::Axis::RX);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_RY) == common::Axis::RY);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_RZ) == common::Axis::RZ);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_HAT_X) == common::Axis::HAT_X);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_HAT_Y) == common::Axis::HAT_Y);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_LTRIGGER) == common::Axis::LTRIGGER);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_RTRIGGER) == common::Axis::RTRIGGER);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_THROTTLE) == common::Axis::THROTTLE);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_RUDDER) == common::Axis::RUDDER);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_WHEEL) == common::Axis::WHEEL);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GAS) == common::Axis::GAS);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_BRAKE) == common::Axis::BRAKE);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_DISTANCE) == common::Axis::DISTANCE);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_TILT) == common::Axis::TILT);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_SCROLL) == common::Axis::SCROLL);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_RELATIVE_X) == common::Axis::RELATIVE_X);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_RELATIVE_Y) == common::Axis::RELATIVE_Y);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_1) == common::Axis::GENERIC_1);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_2) == common::Axis::GENERIC_2);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_3) == common::Axis::GENERIC_3);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_4) == common::Axis::GENERIC_4);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_5) == common::Axis::GENERIC_5);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_6) == common::Axis::GENERIC_6);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_7) == common::Axis::GENERIC_7);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_8) == common::Axis::GENERIC_8);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_9) == common::Axis::GENERIC_9);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_10) == common::Axis::GENERIC_10);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_11) == common::Axis::GENERIC_11);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_12) == common::Axis::GENERIC_12);
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) == common::Axis::GENERIC_13);
+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 common::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
+    common::VideoFrame out;
+    out.width = frame.getWidth();
+    out.height = frame.getHeight();
+    std::vector<char16_t> unsignedData(frame.getData().begin(), frame.getData().end());
+    out.data = unsignedData;
+    struct timeval timestamp = frame.getTimestamp();
+    out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) +
+            microseconds_to_nanoseconds(timestamp.tv_usec);
+    return out;
+}
+
+static std::vector<common::VideoFrame> convertVideoFrames(
+        const std::vector<TouchVideoFrame>& frames) {
+    std::vector<common::VideoFrame> out;
+    for (const TouchVideoFrame& frame : frames) {
+        out.push_back(getHalVideoFrame(frame));
+    }
+    return out;
+}
+
+static void getHalPropertiesAndCoords(const NotifyMotionArgs& args,
+                                      std::vector<common::PointerProperties>& outPointerProperties,
+                                      std::vector<common::PointerCoords>& outPointerCoords) {
+    outPointerProperties.reserve(args.pointerCount);
+    outPointerCoords.reserve(args.pointerCount);
+    for (size_t i = 0; i < args.pointerCount; i++) {
+        common::PointerProperties properties;
+        properties.id = args.pointerProperties[i].id;
+        properties.toolType = getToolType(args.pointerProperties[i].toolType);
+        outPointerProperties.push_back(properties);
+
+        common::PointerCoords coords;
+        // OK to copy bits because we have static_assert for pointerCoords axes
+        coords.bits = args.pointerCoords[i].bits;
+        coords.values = std::vector<float>(args.pointerCoords[i].values,
+                                           args.pointerCoords[i].values +
+                                                   BitSet64::count(args.pointerCoords[i].bits));
+        outPointerCoords.push_back(coords);
+    }
+}
+
+common::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArgs& args) {
+    common::MotionEvent event;
+    event.deviceId = args.deviceId;
+    event.source = getSource(args.source);
+    event.displayId = args.displayId;
+    event.downTime = args.downTime;
+    event.eventTime = args.eventTime;
+    event.deviceTimestamp = 0;
+    event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK);
+    event.actionIndex = MotionEvent::getActionIndex(args.action);
+    event.actionButton = getActionButton(args.actionButton);
+    event.flags = getFlags(args.flags);
+    event.policyFlags = getPolicyFlags(args.policyFlags);
+    event.edgeFlags = getEdgeFlags(args.edgeFlags);
+    event.metaState = getMetastate(args.metaState);
+    event.buttonState = getButtonState(args.buttonState);
+    event.xPrecision = args.xPrecision;
+    event.yPrecision = args.yPrecision;
+
+    std::vector<common::PointerProperties> pointerProperties;
+    std::vector<common::PointerCoords> pointerCoords;
+    getHalPropertiesAndCoords(args, /*out*/ pointerProperties, /*out*/ pointerCoords);
+    event.pointerProperties = pointerProperties;
+    event.pointerCoords = pointerCoords;
+
+    event.frames = convertVideoFrames(args.videoFrames);
+
+    return event;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputClassifierConverter.h b/services/inputflinger/InputCommonConverter.h
similarity index 65%
rename from services/inputflinger/InputClassifierConverter.h
rename to services/inputflinger/InputCommonConverter.h
index 5154b0b..4d3b768 100644
--- a/services/inputflinger/InputClassifierConverter.h
+++ b/services/inputflinger/InputCommonConverter.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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,21 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_CLASSIFIER_CONVERTER_H
-#define _UI_INPUT_CLASSIFIER_CONVERTER_H
+#pragma once
 
+#include <aidl/android/hardware/input/common/Axis.h>
+#include <aidl/android/hardware/input/common/MotionEvent.h>
 #include "InputListener.h"
-#include <android/hardware/input/common/1.0/types.h>
-
 
 namespace android {
 
 /**
- * Convert from framework's NotifyMotionArgs to hidl's common::V1_0::MotionEvent
+ * Convert from framework's NotifyMotionArgs to hidl's common::MotionEvent
  */
-::android::hardware::input::common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(
+::aidl::android::hardware::input::common::MotionEvent notifyMotionArgsToHalMotionEvent(
         const NotifyMotionArgs& args);
 
 } // namespace android
-
-#endif // _UI_INPUT_CLASSIFIER_CONVERTER_H
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 7a9862d..7b03631 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -33,8 +33,6 @@
 namespace android {
 
 using gui::FocusRequest;
-using gui::WindowInfo;
-using gui::WindowInfoHandle;
 
 static int32_t exceptionCodeFromStatusT(status_t status) {
     switch (status) {
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 41e9ce2..cd20a64 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -193,7 +193,6 @@
     void updateInfo() {
         mInfo.token = mClientChannel->getConnectionToken();
         mInfo.name = "FakeWindowHandle";
-        mInfo.type = WindowInfo::Type::APPLICATION;
         mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
         mInfo.frameLeft = mFrame.left;
         mInfo.frameTop = mFrame.top;
@@ -202,10 +201,6 @@
         mInfo.globalScaleFactor = 1.0;
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(mFrame);
-        mInfo.visible = true;
-        mInfo.focusable = true;
-        mInfo.hasWallpaper = false;
-        mInfo.paused = false;
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.displayId = ADISPLAY_ID_DEFAULT;
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index 600f02b..a02b3e8 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -148,11 +148,11 @@
             continue;
         }
         windowFound = true;
-        if (window->getInfo()->visible) {
+        if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
             // Check if at least a single window is visible.
             visibleWindowFound = true;
         }
-        if (!window->getInfo()->focusable) {
+        if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
             // Check if all windows with the window token are focusable.
             allWindowsAreFocusable = false;
             break;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7062aef..74ccf3f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -118,10 +118,7 @@
 // when an application takes too long to respond and the user has pressed an app switch key.
 constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
 
-// Amount of time to allow for an event to be dispatched (measured since its eventTime)
-// before considering it stale and dropping it.
-const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL // 10sec
-        * HwTimeoutMultiplier();
+const std::chrono::duration STALE_EVENT_TIMEOUT = std::chrono::seconds(10) * HwTimeoutMultiplier();
 
 // Log a warning when an event takes longer than this to process, even if an ANR does not occur.
 constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
@@ -322,10 +319,6 @@
             first->applicationInfo.token == second->applicationInfo.token;
 }
 
-bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
-    return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
-}
-
 std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
                                                    std::shared_ptr<EventEntry> eventEntry,
                                                    int32_t inputTargetFlags) {
@@ -500,17 +493,16 @@
 // Returns true if the given window can accept pointer events at the given display location.
 bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y,
                           bool isStylus) {
-    if (windowInfo.displayId != displayId || !windowInfo.visible) {
+    const auto inputConfig = windowInfo.inputConfig;
+    if (windowInfo.displayId != displayId ||
+        inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
         return false;
     }
-    const auto flags = windowInfo.flags;
     const bool windowCanInterceptTouch = isStylus && windowInfo.interceptsStylus();
-    if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
+    if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
         return false;
     }
-    const bool isModalWindow = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
-            !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);
-    if (!isModalWindow && !windowInfo.touchableRegionContainsPoint(x, y)) {
+    if (!windowInfo.touchableRegionContainsPoint(x, y)) {
         return false;
     }
     return true;
@@ -527,6 +519,10 @@
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
+      : InputDispatcher(policy, STALE_EVENT_TIMEOUT) {}
+
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+                                 std::chrono::nanoseconds staleEventTimeout)
       : mPolicy(policy),
         mPendingEvent(nullptr),
         mLastDropReason(DropReason::NOT_DROPPED),
@@ -545,6 +541,7 @@
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
+        mStaleEventTimeout(staleEventTimeout),
         mLatencyAggregator(),
         mLatencyTracker(&mLatencyAggregator) {
     mLooper = new Looper(false);
@@ -902,6 +899,10 @@
     }
 }
 
+bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+    return std::chrono::nanoseconds(currentTime - entry.eventTime) >= mStaleEventTimeout;
+}
+
 /**
  * Return true if the events preceding this incoming motion event should be dropped
  * Return false otherwise (the default behaviour)
@@ -1047,7 +1048,8 @@
             return windowHandle;
         }
 
-        if (addOutsideTargets && info.flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
+        if (addOutsideTargets &&
+            info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
             touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
                                           BitSet32(0));
         }
@@ -1900,7 +1902,8 @@
         return InputEventInjectionResult::PERMISSION_DENIED;
     }
 
-    if (focusedWindowHandle->getInfo()->paused) {
+    if (focusedWindowHandle->getInfo()->inputConfig.test(
+                WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
         ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
         return InputEventInjectionResult::PENDING;
     }
@@ -2101,7 +2104,7 @@
         for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
             const WindowInfo& info = *windowHandle->getInfo();
 
-            if (info.paused) {
+            if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
                 ALOGI("Not sending touch event to %s because it is paused",
                       windowHandle->getName().c_str());
                 continue;
@@ -2324,13 +2327,16 @@
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<WindowInfoHandle> foregroundWindowHandle =
                 tempTouchState.getFirstForegroundWindowHandle();
-        if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
+        if (foregroundWindowHandle &&
+            foregroundWindowHandle->getInfo()->inputConfig.test(
+                    WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
             const std::vector<sp<WindowInfoHandle>>& windowHandles =
                     getWindowHandlesLocked(displayId);
             for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
                 const WindowInfo* info = windowHandle->getInfo();
                 if (info->displayId == displayId &&
-                    windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) {
+                    windowHandle->getInfo()->inputConfig.test(
+                            WindowInfo::InputConfig::IS_WALLPAPER)) {
                     tempTouchState
                             .addOrUpdateWindow(windowHandle,
                                                InputTarget::FLAG_WINDOW_IS_OBSCURED |
@@ -2599,9 +2605,10 @@
     }
     auto info = windowHandle->getInfo();
     auto otherInfo = otherHandle->getInfo();
-    if (!otherInfo->visible) {
+    if (otherInfo->inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
         return false;
-    } else if (otherInfo->alpha == 0 && otherInfo->flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
+    } else if (otherInfo->alpha == 0 &&
+               otherInfo->inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE)) {
         // Those act as if they were invisible, so we don't need to flag them.
         // We do want to potentially flag touchable windows even if they have 0
         // opacity, since they can consume touches and alter the effects of the
@@ -2613,7 +2620,7 @@
         // If ownerUid is the same we don't generate occlusion events as there
         // is no security boundary within an uid.
         return false;
-    } else if (otherInfo->trustedOverlay) {
+    } else if (otherInfo->inputConfig.test(gui::WindowInfo::InputConfig::TRUSTED_OVERLAY)) {
         return false;
     } else if (otherInfo->displayId != info->displayId) {
         return false;
@@ -2694,17 +2701,17 @@
 std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info,
                                                          bool isTouchedWindow) const {
     return StringPrintf(INDENT2
-                        "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
+                        "* %spackage=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
                         "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
-                        "], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, "
+                        "], touchableRegion=%s, window={%s}, inputConfig={%s}, inputFeatures={%s}, "
                         "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
-                        isTouchedWindow ? "[TOUCHED] " : "", ftl::enum_string(info->type).c_str(),
-                        info->packageName.c_str(), info->ownerUid, info->id,
-                        toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
-                        info->frameTop, info->frameRight, info->frameBottom,
-                        dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
-                        info->flags.string().c_str(), info->inputFeatures.string().c_str(),
-                        toString(info->token != nullptr), info->applicationInfo.name.c_str(),
+                        isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(),
+                        info->ownerUid, info->id, toString(info->touchOcclusionMode).c_str(),
+                        info->alpha, info->frameLeft, info->frameTop, info->frameRight,
+                        info->frameBottom, dumpRegion(info->touchableRegion).c_str(),
+                        info->name.c_str(), info->inputConfig.string().c_str(),
+                        info->inputFeatures.string().c_str(), toString(info->token != nullptr),
+                        info->applicationInfo.name.c_str(),
                         toString(info->applicationInfo.token).c_str());
 }
 
@@ -4572,8 +4579,9 @@
         if (getInputChannelLocked(handle->getToken()) == nullptr) {
             const bool noInputChannel =
                     info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
-            const bool canReceiveInput = !info->flags.test(WindowInfo::Flag::NOT_TOUCHABLE) ||
-                    !info->flags.test(WindowInfo::Flag::NOT_FOCUSABLE);
+            const bool canReceiveInput =
+                    !info->inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) ||
+                    !info->inputConfig.test(WindowInfo::InputConfig::NOT_FOCUSABLE);
             if (canReceiveInput && !noInputChannel) {
                 ALOGV("Window handle %s has no registered input channel",
                       handle->getName().c_str());
@@ -4644,12 +4652,16 @@
         }
 
         // Ensure all spy windows are trusted overlays
-        LOG_ALWAYS_FATAL_IF(info.isSpy() && !info.trustedOverlay,
+        LOG_ALWAYS_FATAL_IF(info.isSpy() &&
+                                    !info.inputConfig.test(
+                                            WindowInfo::InputConfig::TRUSTED_OVERLAY),
                             "%s has feature SPY, but is not a trusted overlay.",
                             window->getName().c_str());
 
         // Ensure all stylus interceptors are trusted overlays
-        LOG_ALWAYS_FATAL_IF(info.interceptsStylus() && !info.trustedOverlay,
+        LOG_ALWAYS_FATAL_IF(info.interceptsStylus() &&
+                                    !info.inputConfig.test(
+                                            WindowInfo::InputConfig::TRUSTED_OVERLAY),
                             "%s has feature INTERCEPTS_STYLUS, but is not a trusted overlay.",
                             window->getName().c_str());
     }
@@ -4699,7 +4711,8 @@
                     // Since we are about to drop the touch, cancel the events for the wallpaper as
                     // well.
                     if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND &&
-                        touchedWindow.windowHandle->getInfo()->hasWallpaper) {
+                        touchedWindow.windowHandle->getInfo()->inputConfig.test(
+                                gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
                         sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
                         if (wallpaper != nullptr) {
                             sp<Connection> wallpaperConnection =
@@ -5194,34 +5207,27 @@
                     const WindowInfo* windowInfo = windowHandle->getInfo();
 
                     dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
-                                                 "paused=%s, focusable=%s, "
-                                                 "hasWallpaper=%s, visible=%s, alpha=%.2f, "
-                                                 "flags=%s, type=%s, "
+                                                 "inputConfig=%s, alpha=%.2f, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
                                                  "applicationInfo.name=%s, "
                                                  "applicationInfo.token=%s, "
                                                  "touchableRegion=",
                                          i, windowInfo->name.c_str(), windowInfo->id,
-                                         windowInfo->displayId, toString(windowInfo->paused),
-                                         toString(windowInfo->focusable),
-                                         toString(windowInfo->hasWallpaper),
-                                         toString(windowInfo->visible), windowInfo->alpha,
-                                         windowInfo->flags.string().c_str(),
-                                         ftl::enum_string(windowInfo->type).c_str(),
-                                         windowInfo->frameLeft, windowInfo->frameTop,
-                                         windowInfo->frameRight, windowInfo->frameBottom,
-                                         windowInfo->globalScaleFactor,
+                                         windowInfo->displayId,
+                                         windowInfo->inputConfig.string().c_str(),
+                                         windowInfo->alpha, windowInfo->frameLeft,
+                                         windowInfo->frameTop, windowInfo->frameRight,
+                                         windowInfo->frameBottom, windowInfo->globalScaleFactor,
                                          windowInfo->applicationInfo.name.c_str(),
                                          toString(windowInfo->applicationInfo.token).c_str());
                     dump += dumpRegion(windowInfo->touchableRegion);
                     dump += StringPrintf(", inputFeatures=%s",
                                          windowInfo->inputFeatures.string().c_str());
                     dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
-                                         "ms, trustedOverlay=%s, hasToken=%s, "
+                                         "ms, hasToken=%s, "
                                          "touchOcclusionMode=%s\n",
                                          windowInfo->ownerPid, windowInfo->ownerUid,
                                          millis(windowInfo->dispatchingTimeout),
-                                         toString(windowInfo->trustedOverlay),
                                          toString(windowInfo->token != nullptr),
                                          toString(windowInfo->touchOcclusionMode).c_str());
                     windowInfo->transform.dump(dump, "transform", INDENT4);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e162c78..ae1e1d2 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -84,6 +84,8 @@
     static constexpr bool kDefaultInTouchMode = true;
 
     explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+    explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+                             std::chrono::nanoseconds staleEventTimeout);
     ~InputDispatcher() override;
 
     void dump(std::string& dump) override;
@@ -471,6 +473,11 @@
      */
     std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
 
+    // Amount of time to allow for an event to be dispatched (measured since its eventTime)
+    // before considering it stale and dropping it.
+    const std::chrono::nanoseconds mStaleEventTimeout;
+    bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry);
+
     bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
 
     /**
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index b63fe10..61e78cc 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -100,7 +100,8 @@
     for (const TouchedWindow& window : windows) {
         if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
             if (haveSlipperyForegroundWindow ||
-                !window.windowHandle->getInfo()->flags.test(WindowInfo::Flag::SLIPPERY)) {
+                !window.windowHandle->getInfo()->inputConfig.test(
+                        WindowInfo::InputConfig::SLIPPERY)) {
                 return false;
             }
             haveSlipperyForegroundWindow = true;
@@ -112,7 +113,8 @@
 sp<WindowInfoHandle> TouchState::getWallpaperWindow() const {
     for (size_t i = 0; i < windows.size(); i++) {
         const TouchedWindow& window = windows[i];
-        if (window.windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) {
+        if (window.windowHandle->getInfo()->inputConfig.test(
+                    gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
             return window.windowHandle;
         }
     }
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 51546ce..3bd3275 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -71,6 +71,7 @@
         "libstatslog",
         "libui",
         "libutils",
+        "PlatformProperties",
     ],
     static_libs: [
         "libc++fs",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index db67877..8bd3899 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -685,7 +685,7 @@
     mEpollFd = epoll_create1(EPOLL_CLOEXEC);
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
-    mINotifyFd = inotify_init();
+    mINotifyFd = inotify_init1(IN_CLOEXEC);
 
     std::error_code errorCode;
     bool isDeviceInotifyAdded = false;
@@ -713,7 +713,7 @@
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
     int wakeFds[2];
-    result = pipe(wakeFds);
+    result = pipe2(wakeFds, O_CLOEXEC);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
 
     mWakeReadPipeFd = wakeFds[0];
diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp
index c7c8e28..2f8138b 100644
--- a/services/inputflinger/reader/TouchVideoDevice.cpp
+++ b/services/inputflinger/reader/TouchVideoDevice.cpp
@@ -49,7 +49,7 @@
 };
 
 std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePath) {
-    unique_fd fd(open(devicePath.c_str(), O_RDWR | O_NONBLOCK));
+    unique_fd fd(open(devicePath.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC));
     if (fd.get() == INVALID_FD) {
         ALOGE("Could not open video device %s: %s", devicePath.c_str(), strerror(errno));
         return nullptr;
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index a8e9bae..7d30d0c 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -335,12 +335,15 @@
     // button will likely wake the device.
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
+    int32_t displayId = ADISPLAY_ID_NONE;
+    if (getDeviceContext().getAssociatedViewport()) {
+        displayId = getDeviceContext().getAssociatedViewport()->displayId;
+    }
 
     NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
-                          AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
-                          AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
-                          MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                          &pointerProperties, &pointerCoords, 0, 0,
+                          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);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index ff3a592..8f5dc9b 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -18,6 +18,8 @@
 
 #include "MultiTouchInputMapper.h"
 
+#include <android/sysprop/InputProperties.sysprop.h>
+
 namespace android {
 
 // --- Constants ---
@@ -29,23 +31,15 @@
 
 MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
       : mCurrentSlot(-1),
-        mSlots(nullptr),
-        mSlotCount(0),
         mUsingSlotsProtocol(false),
         mHaveStylus(false) {}
 
-MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() {
-    delete[] mSlots;
-}
-
 void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
                                             bool usingSlotsProtocol) {
-    mSlotCount = slotCount;
     mUsingSlotsProtocol = usingSlotsProtocol;
     mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
 
-    delete[] mSlots;
-    mSlots = new Slot[slotCount];
+    mSlots = std::vector<Slot>(slotCount);
 }
 
 void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
@@ -74,10 +68,8 @@
 }
 
 void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
-    if (mSlots) {
-        for (size_t i = 0; i < mSlotCount; i++) {
-            mSlots[i].clear();
-        }
+    for (Slot& slot : mSlots) {
+        slot.clear();
     }
     mCurrentSlot = initialSlot;
 }
@@ -94,68 +86,68 @@
             mCurrentSlot = 0;
         }
 
-        if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
+        if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) {
             if (DEBUG_POINTERS) {
                 if (newSlot) {
                     ALOGW("MultiTouch device emitted invalid slot index %d but it "
                           "should be between 0 and %zd; ignoring this slot.",
-                          mCurrentSlot, mSlotCount - 1);
+                          mCurrentSlot, mSlots.size() - 1);
                 }
             }
         } else {
-            Slot* slot = &mSlots[mCurrentSlot];
+            Slot& slot = mSlots[mCurrentSlot];
             // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
             // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
             // updating the slot.
             if (!mUsingSlotsProtocol) {
-                slot->mInUse = true;
+                slot.mInUse = true;
             }
 
             switch (rawEvent->code) {
                 case ABS_MT_POSITION_X:
-                    slot->mAbsMTPositionX = rawEvent->value;
-                    warnIfNotInUse(*rawEvent, *slot);
+                    slot.mAbsMTPositionX = rawEvent->value;
+                    warnIfNotInUse(*rawEvent, slot);
                     break;
                 case ABS_MT_POSITION_Y:
-                    slot->mAbsMTPositionY = rawEvent->value;
-                    warnIfNotInUse(*rawEvent, *slot);
+                    slot.mAbsMTPositionY = rawEvent->value;
+                    warnIfNotInUse(*rawEvent, slot);
                     break;
                 case ABS_MT_TOUCH_MAJOR:
-                    slot->mAbsMTTouchMajor = rawEvent->value;
+                    slot.mAbsMTTouchMajor = rawEvent->value;
                     break;
                 case ABS_MT_TOUCH_MINOR:
-                    slot->mAbsMTTouchMinor = rawEvent->value;
-                    slot->mHaveAbsMTTouchMinor = true;
+                    slot.mAbsMTTouchMinor = rawEvent->value;
+                    slot.mHaveAbsMTTouchMinor = true;
                     break;
                 case ABS_MT_WIDTH_MAJOR:
-                    slot->mAbsMTWidthMajor = rawEvent->value;
+                    slot.mAbsMTWidthMajor = rawEvent->value;
                     break;
                 case ABS_MT_WIDTH_MINOR:
-                    slot->mAbsMTWidthMinor = rawEvent->value;
-                    slot->mHaveAbsMTWidthMinor = true;
+                    slot.mAbsMTWidthMinor = rawEvent->value;
+                    slot.mHaveAbsMTWidthMinor = true;
                     break;
                 case ABS_MT_ORIENTATION:
-                    slot->mAbsMTOrientation = rawEvent->value;
+                    slot.mAbsMTOrientation = rawEvent->value;
                     break;
                 case ABS_MT_TRACKING_ID:
                     if (mUsingSlotsProtocol && rawEvent->value < 0) {
                         // The slot is no longer in use but it retains its previous contents,
                         // which may be reused for subsequent touches.
-                        slot->mInUse = false;
+                        slot.mInUse = false;
                     } else {
-                        slot->mInUse = true;
-                        slot->mAbsMTTrackingId = rawEvent->value;
+                        slot.mInUse = true;
+                        slot.mAbsMTTrackingId = rawEvent->value;
                     }
                     break;
                 case ABS_MT_PRESSURE:
-                    slot->mAbsMTPressure = rawEvent->value;
+                    slot.mAbsMTPressure = rawEvent->value;
                     break;
                 case ABS_MT_DISTANCE:
-                    slot->mAbsMTDistance = rawEvent->value;
+                    slot.mAbsMTDistance = rawEvent->value;
                     break;
                 case ABS_MT_TOOL_TYPE:
-                    slot->mAbsMTToolType = rawEvent->value;
-                    slot->mHaveAbsMTToolType = true;
+                    slot.mAbsMTToolType = rawEvent->value;
+                    slot.mHaveAbsMTToolType = true;
                     break;
             }
         }
@@ -184,28 +176,6 @@
 
 // --- MultiTouchMotionAccumulator::Slot ---
 
-MultiTouchMotionAccumulator::Slot::Slot() {
-    clear();
-}
-
-void MultiTouchMotionAccumulator::Slot::clear() {
-    mInUse = false;
-    mHaveAbsMTTouchMinor = false;
-    mHaveAbsMTWidthMinor = false;
-    mHaveAbsMTToolType = false;
-    mAbsMTPositionX = 0;
-    mAbsMTPositionY = 0;
-    mAbsMTTouchMajor = 0;
-    mAbsMTTouchMinor = 0;
-    mAbsMTWidthMajor = 0;
-    mAbsMTWidthMinor = 0;
-    mAbsMTOrientation = 0;
-    mAbsMTTrackingId = -1;
-    mAbsMTPressure = 0;
-    mAbsMTDistance = 0;
-    mAbsMTToolType = 0;
-}
-
 int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
     if (mHaveAbsMTToolType) {
         switch (mAbsMTToolType) {
@@ -262,14 +232,14 @@
     mHavePointerIds = true;
 
     for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
-        const MultiTouchMotionAccumulator::Slot* inSlot =
+        const MultiTouchMotionAccumulator::Slot& inSlot =
                 mMultiTouchMotionAccumulator.getSlot(inIndex);
-        if (!inSlot->isInUse()) {
+        if (!inSlot.isInUse()) {
             continue;
         }
 
-        if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
-            std::optional<int32_t> id = getActiveBitId(*inSlot);
+        if (inSlot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
+            std::optional<int32_t> id = getActiveBitId(inSlot);
             if (id) {
                 outState->rawPointerData.canceledIdBits.markBit(id.value());
             }
@@ -290,34 +260,38 @@
         }
 
         RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
-        outPointer.x = inSlot->getX();
-        outPointer.y = inSlot->getY();
-        outPointer.pressure = inSlot->getPressure();
-        outPointer.touchMajor = inSlot->getTouchMajor();
-        outPointer.touchMinor = inSlot->getTouchMinor();
-        outPointer.toolMajor = inSlot->getToolMajor();
-        outPointer.toolMinor = inSlot->getToolMinor();
-        outPointer.orientation = inSlot->getOrientation();
-        outPointer.distance = inSlot->getDistance();
+        outPointer.x = inSlot.getX();
+        outPointer.y = inSlot.getY();
+        outPointer.pressure = inSlot.getPressure();
+        outPointer.touchMajor = inSlot.getTouchMajor();
+        outPointer.touchMinor = inSlot.getTouchMinor();
+        outPointer.toolMajor = inSlot.getToolMajor();
+        outPointer.toolMinor = inSlot.getToolMinor();
+        outPointer.orientation = inSlot.getOrientation();
+        outPointer.distance = inSlot.getDistance();
         outPointer.tiltX = 0;
         outPointer.tiltY = 0;
 
-        outPointer.toolType = inSlot->getToolType();
+        outPointer.toolType = inSlot.getToolType();
         if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
             outPointer.toolType = mTouchButtonAccumulator.getToolType();
             if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
                 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
             }
         }
+        if (shouldSimulateStylusWithTouch() &&
+            outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
+            outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+        }
 
         bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
                 (mTouchButtonAccumulator.isHovering() ||
-                 (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
+                 (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
         outPointer.isHovering = isHovering;
 
         // Assign pointer id using tracking id if available.
         if (mHavePointerIds) {
-            int32_t trackingId = inSlot->getTrackingId();
+            int32_t trackingId = inSlot.getTrackingId();
             int32_t id = -1;
             if (trackingId >= 0) {
                 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
@@ -385,7 +359,15 @@
 }
 
 bool MultiTouchInputMapper::hasStylus() const {
-    return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus();
+    return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
+            shouldSimulateStylusWithTouch();
+}
+
+bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
+    static const bool SIMULATE_STYLUS_WITH_TOUCH =
+            sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
+    return SIMULATE_STYLUS_WITH_TOUCH &&
+            mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 225ad49..fe8af5d 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -46,29 +46,27 @@
     private:
         friend class MultiTouchMotionAccumulator;
 
-        bool mInUse;
-        bool mHaveAbsMTTouchMinor;
-        bool mHaveAbsMTWidthMinor;
-        bool mHaveAbsMTToolType;
+        bool mInUse = false;
+        bool mHaveAbsMTTouchMinor = false;
+        bool mHaveAbsMTWidthMinor = false;
+        bool mHaveAbsMTToolType = false;
 
-        int32_t mAbsMTPositionX;
-        int32_t mAbsMTPositionY;
-        int32_t mAbsMTTouchMajor;
-        int32_t mAbsMTTouchMinor;
-        int32_t mAbsMTWidthMajor;
-        int32_t mAbsMTWidthMinor;
-        int32_t mAbsMTOrientation;
-        int32_t mAbsMTTrackingId;
-        int32_t mAbsMTPressure;
-        int32_t mAbsMTDistance;
-        int32_t mAbsMTToolType;
+        int32_t mAbsMTPositionX = 0;
+        int32_t mAbsMTPositionY = 0;
+        int32_t mAbsMTTouchMajor = 0;
+        int32_t mAbsMTTouchMinor = 0;
+        int32_t mAbsMTWidthMajor = 0;
+        int32_t mAbsMTWidthMinor = 0;
+        int32_t mAbsMTOrientation = 0;
+        int32_t mAbsMTTrackingId = -1;
+        int32_t mAbsMTPressure = 0;
+        int32_t mAbsMTDistance = 0;
+        int32_t mAbsMTToolType = 0;
 
-        Slot();
-        void clear();
+        void clear() { *this = Slot(); }
     };
 
     MultiTouchMotionAccumulator();
-    ~MultiTouchMotionAccumulator();
 
     void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
     void reset(InputDeviceContext& deviceContext);
@@ -76,13 +74,15 @@
     void finishSync();
     bool hasStylus() const;
 
-    inline size_t getSlotCount() const { return mSlotCount; }
-    inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
+    inline size_t getSlotCount() const { return mSlots.size(); }
+    inline const Slot& getSlot(size_t index) const {
+        LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index);
+        return mSlots[index];
+    }
 
 private:
     int32_t mCurrentSlot;
-    Slot* mSlots;
-    size_t mSlotCount;
+    std::vector<Slot> mSlots;
     bool mUsingSlotsProtocol;
     bool mHaveStylus;
 
@@ -104,6 +104,14 @@
     bool hasStylus() const override;
 
 private:
+    // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
+    // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
+    // It is used to simulate stylus events for debugging and testing on a device that does not
+    // support styluses. It can be enabled using
+    // "adb shell setprop persist.debug.input.simulate_stylus_with_touch true",
+    // and requires a reboot to take effect.
+    inline bool shouldSimulateStylusWithTouch() const;
+
     // If the slot is in use, return the bit id. Return std::nullopt otherwise.
     std::optional<int32_t> getActiveBitId(const MultiTouchMotionAccumulator::Slot& inSlot);
     MultiTouchMotionAccumulator mMultiTouchMotionAccumulator;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f729ba9..c1934ff 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -550,18 +550,15 @@
 
 /**
  * Determine which DisplayViewport to use.
- * 1. If display port is specified, return the matching viewport. If matching viewport not
- * found, then return.
+ * 1. If a device has associated display, get the matching viewport.
  * 2. Always use the suggested viewport from WindowManagerService for pointers.
- * 3. If a device has associated display, get the matching viewport by either unique id or by
- * the display type (internal or external).
+ * 3. Get the matching viewport by either unique id in idc file or by the display type
+ * (internal or external).
  * 4. Otherwise, use a non-display viewport.
  */
 std::optional<DisplayViewport> TouchInputMapper::findViewport() {
     if (mParameters.hasAssociatedDisplay && mDeviceMode != DeviceMode::UNSCALED) {
-        const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
-        if (displayPort) {
-            // Find the viewport that contains the same port
+        if (getDeviceContext().getAssociatedViewport()) {
             return getDeviceContext().getAssociatedViewport();
         }
 
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index 662be80..ffce9f6 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -37,12 +37,16 @@
                      bool visible) {
         mInfo.token = token;
         mInfo.name = name;
-        mInfo.visible = visible;
-        mInfo.focusable = focusable;
+        setFocusable(focusable);
+        setVisible(visible);
     }
 
-    void setFocusable(bool focusable) { mInfo.focusable = focusable; }
-    void setVisible(bool visible) { mInfo.visible = visible; }
+    void setFocusable(bool focusable) {
+        mInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable);
+    }
+    void setVisible(bool visible) {
+        mInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+    }
 };
 
 TEST(FocusResolverTest, SetFocusedWindow) {
diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp
index f626d56..81ef9b9 100644
--- a/services/inputflinger/tests/InputClassifierConverter_test.cpp
+++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#include "../InputClassifierConverter.h"
+#include "../InputCommonConverter.h"
 
 #include <gtest/gtest.h>
 #include <gui/constants.h>
 #include <utils/BitSet.h>
 
-using namespace android::hardware::input;
+using namespace aidl::android::hardware::input;
 
 namespace android {
 
@@ -50,8 +50,7 @@
     return motionArgs;
 }
 
-static float getMotionEventAxis(common::V1_0::PointerCoords coords,
-        common::V1_0::Axis axis) {
+static float getMotionEventAxis(common::PointerCoords coords, common::Axis axis) {
     uint32_t index = BitSet64::getIndexOfBit(static_cast<uint64_t>(coords.bits),
             static_cast<uint64_t>(axis));
     return coords.values[index];
@@ -68,14 +67,14 @@
     ASSERT_EQ(0.5, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE));
     ASSERT_EQ(3U, BitSet64::count(motionArgs.pointerCoords[0].bits));
 
-    common::V1_0::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(motionArgs);
+    common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(motionArgs);
 
-    ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::X),
-            motionArgs.pointerCoords[0].getX());
-    ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::Y),
-            motionArgs.pointerCoords[0].getY());
-    ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::SIZE),
-            motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE));
+    ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::Axis::X),
+              motionArgs.pointerCoords[0].getX());
+    ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::Axis::Y),
+              motionArgs.pointerCoords[0].getY());
+    ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::Axis::SIZE),
+              motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE));
     ASSERT_EQ(BitSet64::count(motionArgs.pointerCoords[0].bits),
             BitSet64::count(motionEvent.pointerCoords[0].bits));
 }
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index f13187d..3a77127 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -20,12 +20,14 @@
 
 #include "TestInputListener.h"
 
-#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+#include <aidl/android/hardware/input/processor/BnInputProcessor.h>
+#include <aidl/android/hardware/input/processor/IInputProcessor.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 
-using namespace android::hardware::input;
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::input::common::V1_0::Classification;
+using namespace aidl::android::hardware::input;
+using aidl::android::hardware::input::common::Classification;
+using aidl::android::hardware::input::processor::IInputProcessor;
 
 namespace android {
 
@@ -154,22 +156,17 @@
 /**
  * A minimal implementation of IInputClassifier.
  */
-struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier {
-    Return<Classification> classify(
-            const android::hardware::input::common::V1_0::MotionEvent& event) override {
-        return Classification::NONE;
-    };
-    Return<void> reset() override { return Void(); };
-    Return<void> resetDevice(int32_t deviceId) override { return Void(); };
-};
-
-/**
- * An entity that will be subscribed to the HAL death.
- */
-class TestDeathRecipient : public android::hardware::hidl_death_recipient {
-public:
-    virtual void serviceDied(uint64_t cookie,
-                             const wp<android::hidl::base::V1_0::IBase>& who) override{};
+class TestHal : public aidl::android::hardware::input::processor::BnInputProcessor {
+    ::ndk::ScopedAStatus classify(
+            const ::aidl::android::hardware::input::common::MotionEvent& in_event,
+            ::aidl::android::hardware::input::common::Classification* _aidl_return) override {
+        *_aidl_return = Classification::NONE;
+        return ndk::ScopedAStatus::ok();
+    }
+    ::ndk::ScopedAStatus reset() override { return ndk::ScopedAStatus::ok(); }
+    ::ndk::ScopedAStatus resetDevice(int32_t in_deviceId) override {
+        return ndk::ScopedAStatus::ok();
+    }
 };
 
 // --- MotionClassifierTest ---
@@ -178,15 +175,9 @@
 protected:
     std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
 
-    virtual void SetUp() override {
-        mMotionClassifier = MotionClassifier::create(new TestDeathRecipient());
-        if (mMotionClassifier == nullptr) {
-            // If the device running this test does not have IInputClassifier service,
-            // use the test HAL instead.
-            // Using 'new' to access non-public constructor
-            mMotionClassifier =
-                    std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal()));
-        }
+    void SetUp() override {
+        std::shared_ptr<IInputProcessor> service = ndk::SharedRefBase::make<TestHal>();
+        mMotionClassifier = MotionClassifier::create(std::move(service));
     }
 };
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 872882e..1ffa875 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -21,6 +21,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
+#include <fcntl.h>
 #include <gtest/gtest.h>
 #include <input/Input.h>
 #include <linux/input.h>
@@ -59,6 +60,8 @@
 // An arbitrary pid of the gesture monitor window
 static constexpr int32_t MONITOR_PID = 2001;
 
+static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+
 struct PointF {
     float x;
     float y;
@@ -492,7 +495,7 @@
 
     void SetUp() override {
         mFakePolicy = new FakeInputDispatcherPolicy();
-        mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
+        mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy, STALE_EVENT_TIMEOUT);
         mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
         // Start InputDispatcher thread
         ASSERT_EQ(OK, mDispatcher->start());
@@ -986,7 +989,6 @@
         mInfo.token = *token;
         mInfo.id = sId++;
         mInfo.name = name;
-        mInfo.type = WindowInfo::Type::APPLICATION;
         mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
         mInfo.alpha = 1.0;
         mInfo.frameLeft = 0;
@@ -997,14 +999,10 @@
         mInfo.globalScaleFactor = 1.0;
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
-        mInfo.visible = true;
-        mInfo.focusable = false;
-        mInfo.hasWallpaper = false;
-        mInfo.paused = false;
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.displayId = displayId;
-        mInfo.trustedOverlay = false;
+        mInfo.inputConfig = WindowInfo::InputConfig::NONE;
     }
 
     sp<FakeWindowHandle> clone(
@@ -1016,15 +1014,37 @@
         return handle;
     }
 
-    void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+    void setTouchable(bool touchable) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable);
+    }
 
-    void setVisible(bool visible) { mInfo.visible = visible; }
+    void setFocusable(bool focusable) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable);
+    }
+
+    void setVisible(bool visible) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+    }
 
     void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
         mInfo.dispatchingTimeout = timeout;
     }
 
-    void setPaused(bool paused) { mInfo.paused = paused; }
+    void setPaused(bool paused) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::PAUSE_DISPATCHING, paused);
+    }
+
+    void setPreventSplitting(bool preventSplitting) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::PREVENT_SPLITTING, preventSplitting);
+    }
+
+    void setSlippery(bool slippery) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::SLIPPERY, slippery);
+    }
+
+    void setWatchOutsideTouch(bool watchOutside) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
+    }
 
     void setAlpha(float alpha) { mInfo.alpha = alpha; }
 
@@ -1048,17 +1068,19 @@
 
     void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
 
-    void setType(WindowInfo::Type type) { mInfo.type = type; }
+    void setIsWallpaper(bool isWallpaper) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::IS_WALLPAPER, isWallpaper);
+    }
 
-    void setHasWallpaper(bool hasWallpaper) { mInfo.hasWallpaper = hasWallpaper; }
-
-    void addFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags |= flags; }
-
-    void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; }
+    void setDupTouchToWallpaper(bool hasWallpaper) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
+    }
 
     void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; }
 
-    void setTrustedOverlay(bool trustedOverlay) { mInfo.trustedOverlay = trustedOverlay; }
+    void setTrustedOverlay(bool trustedOverlay) {
+        mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay);
+    }
 
     void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
         mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
@@ -1539,19 +1561,15 @@
 }
 
 /**
- * Calling setInputWindows once with FLAG_NOT_TOUCH_MODAL should not cause any issues.
- * To ensure that window receives only events that were directly inside of it, add
- * FLAG_NOT_TOUCH_MODAL. This will enforce using the touchableRegion of the input
- * when finding touched windows.
+ * Calling setInputWindows once should not cause any issues.
  * This test serves as a sanity check for the next test, where setInputWindows is
  * called twice.
  */
-TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) {
+TEST_F(InputDispatcherTest, SetInputWindowOnceWithSingleTouchWindow) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
     window->setFrame(Rect(0, 0, 100, 100));
-    window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1565,16 +1583,12 @@
 
 /**
  * Calling setInputWindows twice, with the same info, should not cause any issues.
- * To ensure that window receives only events that were directly inside of it, add
- * FLAG_NOT_TOUCH_MODAL. This will enforce using the touchableRegion of the input
- * when finding touched windows.
  */
 TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
     window->setFrame(Rect(0, 0, 100, 100));
-    window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1609,17 +1623,17 @@
  * Two windows: A top window, and a wallpaper behind the window.
  * Touch goes to the top window, and then top window disappears. Ensure that wallpaper window
  * gets ACTION_CANCEL.
- * 1. foregroundWindow <-- has wallpaper (hasWallpaper=true)
- * 2. wallpaperWindow <-- is wallpaper (type=InputWindowInfo::Type::WALLPAPER)
+ * 1. foregroundWindow <-- dup touch to wallpaper
+ * 2. wallpaperWindow <-- is wallpaper
  */
 TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> foregroundWindow =
             new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
-    foregroundWindow->setHasWallpaper(true);
+    foregroundWindow->setDupTouchToWallpaper(true);
     sp<FakeWindowHandle> wallpaperWindow =
             new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
-    wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+    wallpaperWindow->setIsWallpaper(true);
     constexpr int expectedWallpaperFlags =
             AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
@@ -1660,10 +1674,10 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> foregroundWindow =
             new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
-    foregroundWindow->setHasWallpaper(true);
+    foregroundWindow->setDupTouchToWallpaper(true);
     sp<FakeWindowHandle> wallpaperWindow =
             new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
-    wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+    wallpaperWindow->setIsWallpaper(true);
     constexpr int expectedWallpaperFlags =
             AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
@@ -1704,11 +1718,11 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
-    window->setHasWallpaper(true);
+    window->setDupTouchToWallpaper(true);
 
     sp<FakeWindowHandle> wallpaperWindow =
             new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
-    wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+    wallpaperWindow->setIsWallpaper(true);
     constexpr int expectedWallpaperFlags =
             AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
@@ -1762,19 +1776,17 @@
     sp<FakeWindowHandle> leftWindow =
             new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
     leftWindow->setFrame(Rect(0, 0, 200, 200));
-    leftWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
-    leftWindow->setHasWallpaper(true);
+    leftWindow->setDupTouchToWallpaper(true);
 
     sp<FakeWindowHandle> rightWindow =
             new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
     rightWindow->setFrame(Rect(200, 0, 400, 200));
-    rightWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
-    rightWindow->setHasWallpaper(true);
+    rightWindow->setDupTouchToWallpaper(true);
 
     sp<FakeWindowHandle> wallpaperWindow =
             new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
     wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
-    wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+    wallpaperWindow->setIsWallpaper(true);
     constexpr int expectedWallpaperFlags =
             AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
 
@@ -1848,11 +1860,9 @@
     sp<FakeWindowHandle> windowLeft =
             new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
     windowLeft->setFrame(Rect(0, 0, 600, 800));
-    windowLeft->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
     sp<FakeWindowHandle> windowRight =
             new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
     windowRight->setFrame(Rect(600, 0, 1200, 800));
-    windowRight->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
@@ -1959,7 +1969,6 @@
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
     window->setFrame(Rect(0, 0, 1200, 800));
-    window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
@@ -2041,11 +2050,9 @@
     sp<FakeWindowHandle> windowLeft =
             new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
     windowLeft->setFrame(Rect(0, 0, 600, 800));
-    windowLeft->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
     sp<FakeWindowHandle> windowRight =
             new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
     windowRight->setFrame(Rect(600, 0, 1200, 800));
-    windowRight->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
@@ -2151,14 +2158,12 @@
         // Add two windows to the display. Their frames are represented in the display space.
         sp<FakeWindowHandle> firstWindow =
                 new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
-        firstWindow->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
         firstWindow->setFrame(Rect(0, 0, 100, 200), displayTransform);
         addWindow(firstWindow);
 
         sp<FakeWindowHandle> secondWindow =
                 new FakeWindowHandle(application, mDispatcher, "Second Window",
                                      ADISPLAY_ID_DEFAULT);
-        secondWindow->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
         secondWindow->setFrame(Rect(100, 200, 200, 400), displayTransform);
         addWindow(secondWindow);
         return {std::move(firstWindow), std::move(secondWindow)};
@@ -2301,8 +2306,10 @@
     // Create a couple of windows
     sp<FakeWindowHandle> firstWindow =
             new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+    firstWindow->setPreventSplitting(true);
     sp<FakeWindowHandle> secondWindow =
             new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
+    secondWindow->setPreventSplitting(true);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -2375,17 +2382,13 @@
 TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
-    // Create a non touch modal window that supports split touch
     sp<FakeWindowHandle> firstWindow =
             new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
     firstWindow->setFrame(Rect(0, 0, 600, 400));
-    firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
-    // Create a non touch modal window that supports split touch
     sp<FakeWindowHandle> secondWindow =
             new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
     secondWindow->setFrame(Rect(0, 400, 600, 800));
-    secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -2447,17 +2450,13 @@
 TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
-    // Create a non touch modal window that supports split touch
     sp<FakeWindowHandle> firstWindow =
             new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
     firstWindow->setFrame(Rect(0, 0, 600, 400));
-    firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
-    // Create a non touch modal window that supports split touch
     sp<FakeWindowHandle> secondWindow =
             new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
     secondWindow->setFrame(Rect(0, 400, 600, 800));
-    secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -2522,26 +2521,21 @@
     sp<FakeWindowHandle> firstWindowInPrimary =
             new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
     firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
-    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
     sp<FakeWindowHandle> secondWindowInPrimary =
             new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
     secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
-    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     sp<FakeWindowHandle> mirrorWindowInPrimary =
             firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
     mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
-    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     sp<FakeWindowHandle> firstWindowInSecondary =
             firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
     firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
-    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     sp<FakeWindowHandle> secondWindowInSecondary =
             secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
     secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
-    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     // Update window info, let it find window handle of second display first.
     mDispatcher->setInputWindows(
@@ -2586,26 +2580,21 @@
     sp<FakeWindowHandle> firstWindowInPrimary =
             new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
     firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
-    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
     sp<FakeWindowHandle> secondWindowInPrimary =
             new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
     secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
-    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     sp<FakeWindowHandle> mirrorWindowInPrimary =
             firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
     mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
-    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     sp<FakeWindowHandle> firstWindowInSecondary =
             firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
     firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
-    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     sp<FakeWindowHandle> secondWindowInSecondary =
             secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
     secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
-    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     // Update window info, let it find window handle of second display first.
     mDispatcher->setInputWindows(
@@ -2699,17 +2688,13 @@
 TEST_F(InputDispatcherTest, PointerCancel_SendCancelWhenSplitTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
-    // Create first non touch modal window that supports split touch
     sp<FakeWindowHandle> firstWindow =
             new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
     firstWindow->setFrame(Rect(0, 0, 600, 400));
-    firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
-    // Create second non touch modal window that supports split touch
     sp<FakeWindowHandle> secondWindow =
             new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
     secondWindow->setFrame(Rect(0, 400, 600, 800));
-    secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -3229,9 +3214,9 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+    window->setFocusable(false);
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    // Window is not focusable.
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     setFocusedWindow(window);
 
@@ -3239,7 +3224,7 @@
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
             << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
 
-    // window is invalid, so it should not receive any input event.
+    // window is not focusable, so it should not receive any input event.
     window->assertNoEvents();
 }
 
@@ -3376,7 +3361,7 @@
 
     sp<FakeWindowHandle> slipperyExitWindow =
             new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
-    slipperyExitWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SLIPPERY);
+    slipperyExitWindow->setSlippery(true);
     // Make sure this one overlaps the bottom window
     slipperyExitWindow->setFrame(Rect(25, 25, 75, 75));
     // Change the owner uid/pid of the window so that it is considered to be occluding the bottom
@@ -3982,14 +3967,10 @@
         mUnfocusedWindow =
                 new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
         mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
-        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
-        // window.
-        mUnfocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         mFocusedWindow =
                 new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
         mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
-        mFocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -4096,14 +4077,10 @@
                 std::make_shared<FakeApplicationHandle>();
         mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1",
                                         ADISPLAY_ID_DEFAULT);
-        // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
-        // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
-        mWindow1->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
         mWindow1->setFrame(Rect(0, 0, 100, 100));
 
         mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
                                         ADISPLAY_ID_DEFAULT, mWindow1->getToken());
-        mWindow2->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
         mWindow2->setFrame(Rect(100, 100, 200, 200));
 
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
@@ -4300,9 +4277,6 @@
         mWindow->setFrame(Rect(0, 0, 30, 30));
         mWindow->setDispatchingTimeout(30ms);
         mWindow->setFocusable(true);
-        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
-        // window.
-        mWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
@@ -4438,6 +4412,38 @@
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
+/**
+ * Make sure the stale key is dropped before causing an ANR. So even if there's no focused window,
+ * there will not be an ANR.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, StaleKeyEventDoesNotAnr) {
+    mWindow->setFocusable(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+
+    KeyEvent event;
+    const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC) -
+            std::chrono::nanoseconds(STALE_EVENT_TIMEOUT).count();
+
+    // Define a valid key down event that is stale (too old).
+    event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+                     INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
+                     AMETA_NONE, 1 /*repeatCount*/, eventTime, eventTime);
+
+    const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+
+    InputEventInjectionResult result =
+            mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                          InputEventInjectionSync::WAIT_FOR_RESULT,
+                                          INJECT_EVENT_TIMEOUT, policyFlags);
+    ASSERT_EQ(InputEventInjectionResult::FAILED, result)
+            << "Injection should fail because the event is stale";
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+    mWindow->assertNoEvents();
+}
+
 // We have a focused application, but no focused window
 // Make sure that we don't notify policy twice about the same ANR.
 TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
@@ -4750,18 +4756,13 @@
         mUnfocusedWindow =
                 new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT);
         mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
-        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
-        // window.
         // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
-        mUnfocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL |
-                                   WindowInfo::Flag::WATCH_OUTSIDE_TOUCH |
-                                   WindowInfo::Flag::SPLIT_TOUCH);
+        mUnfocusedWindow->setWatchOutsideTouch(true);
 
         mFocusedWindow =
                 new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
         mFocusedWindow->setDispatchingTimeout(30ms);
         mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
-        mFocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
@@ -5495,7 +5496,7 @@
     sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name, TouchOcclusionMode mode,
                                             float alpha = 1.0f) {
         sp<FakeWindowHandle> window = getWindow(uid, name);
-        window->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+        window->setTouchable(false);
         window->setTouchOcclusionMode(mode);
         window->setAlpha(alpha);
         return window;
@@ -5609,7 +5610,7 @@
        WindowWithZeroOpacityAndWatchOutside_ReceivesOutsideEvent) {
     const sp<FakeWindowHandle>& w =
             getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
-    w->addFlags(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+    w->setWatchOutsideTouch(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
 
     touch();
@@ -5620,7 +5621,7 @@
 TEST_F(InputDispatcherUntrustedTouchesTest, OutsideEvent_HasZeroCoordinates) {
     const sp<FakeWindowHandle>& w =
             getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
-    w->addFlags(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+    w->setWatchOutsideTouch(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
 
     touch();
@@ -5870,11 +5871,9 @@
         mApp = std::make_shared<FakeApplicationHandle>();
         mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
         mWindow->setFrame(Rect(0, 0, 100, 100));
-        mWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
         mSecondWindow->setFrame(Rect(100, 0, 200, 100));
-        mSecondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
@@ -6129,7 +6128,7 @@
                                  ADISPLAY_ID_DEFAULT);
     obscuringWindow->setFrame(Rect(0, 0, 50, 50));
     obscuringWindow->setOwnerInfo(111, 111);
-    obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+    obscuringWindow->setTouchable(false);
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
@@ -6175,7 +6174,7 @@
                                  ADISPLAY_ID_DEFAULT);
     obscuringWindow->setFrame(Rect(0, 0, 50, 50));
     obscuringWindow->setOwnerInfo(111, 111);
-    obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+    obscuringWindow->setTouchable(false);
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
@@ -6224,40 +6223,64 @@
         mApp = std::make_shared<FakeApplicationHandle>();
         mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
         mWindow->setFocusable(true);
+        setFocusedWindow(mWindow);
         mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
         mSecondWindow->setFocusable(true);
 
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
-
-        setFocusedWindow(mWindow);
         mWindow->consumeFocusEvent(true);
+
+        // Set initial touch mode to InputDispatcher::kDefaultInTouchMode.
+        mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, INJECTOR_PID,
+                                    INJECTOR_UID, /* hasPermission */ true);
     }
 
     void changeAndVerifyTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) {
-        mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission);
+        ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission));
         mWindow->consumeTouchModeEvent(inTouchMode);
         mSecondWindow->consumeTouchModeEvent(inTouchMode);
     }
 };
 
-TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchModeOnFocusedWindow) {
+TEST_F(InputDispatcherTouchModeChangedTests, FocusedWindowCanChangeTouchMode) {
     const WindowInfo& windowInfo = *mWindow->getInfo();
     changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid,
                              windowInfo.ownerUid, /* hasPermission */ false);
 }
 
+TEST_F(InputDispatcherTouchModeChangedTests, NonFocusedWindowOwnerCannotChangeTouchMode) {
+    const WindowInfo& windowInfo = *mWindow->getInfo();
+    int32_t ownerPid = windowInfo.ownerPid;
+    int32_t ownerUid = windowInfo.ownerUid;
+    mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
+    ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, ownerPid,
+                                             ownerUid, /* hasPermission */ false));
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTouchModeChangedTests, NonWindowOwnerMayChangeTouchModeOnPermissionGranted) {
+    const WindowInfo& windowInfo = *mWindow->getInfo();
+    int32_t ownerPid = windowInfo.ownerPid;
+    int32_t ownerUid = windowInfo.ownerUid;
+    mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
+    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, ownerPid, ownerUid,
+                             /* hasPermission */ true);
+}
+
 TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) {
     const WindowInfo& windowInfo = *mWindow->getInfo();
-    mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid,
-                                windowInfo.ownerUid, /* hasPermission */ true);
+    ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode,
+                                             windowInfo.ownerPid, windowInfo.ownerUid,
+                                             /* hasPermission */ true));
     mWindow->assertNoEvents();
     mSecondWindow->assertNoEvents();
 }
 
 class InputDispatcherSpyWindowTest : public InputDispatcherTest {
 public:
-    sp<FakeWindowHandle> createSpy(const Flags<WindowInfo::Flag> flags) {
+    sp<FakeWindowHandle> createSpy() {
         std::shared_ptr<FakeApplicationHandle> application =
                 std::make_shared<FakeApplicationHandle>();
         std::string name = "Fake Spy ";
@@ -6266,7 +6289,6 @@
                 new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT);
         spy->setInputFeatures(WindowInfo::Feature::SPY);
         spy->setTrustedOverlay(true);
-        spy->addFlags(flags);
         return spy;
     }
 
@@ -6275,7 +6297,6 @@
                 std::make_shared<FakeApplicationHandle>();
         sp<FakeWindowHandle> window =
                 new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
-        window->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
         window->setFocusable(true);
         return window;
     }
@@ -6291,7 +6312,7 @@
 TEST_F(InputDispatcherSpyWindowDeathTest, UntrustedSpy_AbortsDispatcher) {
     ScopedSilentDeath _silentDeath;
 
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy = createSpy();
     spy->setTrustedOverlay(false);
     ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}),
                  ".* not a trusted overlay");
@@ -6301,7 +6322,7 @@
  * Input injection into a display with a spy window but no foreground windows should succeed.
  */
 TEST_F(InputDispatcherSpyWindowTest, NoForegroundWindow) {
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy = createSpy();
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}});
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6324,14 +6345,14 @@
  */
 TEST_F(InputDispatcherSpyWindowTest, ReceivesInputInOrder) {
     auto window = createForeground();
-    auto spy1 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
-    auto spy2 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
-    auto spy3 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy1 = createSpy();
+    auto spy2 = createSpy();
+    auto spy3 = createSpy();
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window, spy3}}});
     const std::vector<sp<FakeWindowHandle>> channels{spy1, spy2, window, spy3};
     const size_t numChannels = channels.size();
 
-    base::unique_fd epollFd(epoll_create1(0 /*flags*/));
+    base::unique_fd epollFd(epoll_create1(EPOLL_CLOEXEC));
     if (!epollFd.ok()) {
         FAIL() << "Failed to create epoll fd";
     }
@@ -6377,7 +6398,8 @@
  */
 TEST_F(InputDispatcherSpyWindowTest, NotTouchable) {
     auto window = createForeground();
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCHABLE);
+    auto spy = createSpy();
+    spy->setTouchable(false);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6394,7 +6416,7 @@
  */
 TEST_F(InputDispatcherSpyWindowTest, TouchableRegion) {
     auto window = createForeground();
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy = createSpy();
     spy->setTouchableRegion(Region{{0, 0, 20, 20}});
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
 
@@ -6420,32 +6442,14 @@
 }
 
 /**
- * A spy window that is a modal window will receive gestures outside of its frame and touchable
- * region.
- */
-TEST_F(InputDispatcherSpyWindowTest, ModalWindow) {
-    auto window = createForeground();
-    auto spy = createSpy(static_cast<WindowInfo::Flag>(0));
-    // This spy window does not have the NOT_TOUCH_MODAL flag set.
-    spy->setFrame(Rect{0, 0, 20, 20});
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
-
-    // Inject an event outside the spy window's frame and touchable region.
-    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
-              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
-    window->consumeMotionDown();
-    spy->consumeMotionDown();
-}
-
-/**
  * A spy window can listen for touches outside its touchable region using the WATCH_OUTSIDE_TOUCHES
  * flag, but it will get zero-ed out coordinates if the foreground has a different owner.
  */
 TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) {
     auto window = createForeground();
     window->setOwnerInfo(12, 34);
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+    auto spy = createSpy();
+    spy->setWatchOutsideTouch(true);
     spy->setOwnerInfo(56, 78);
     spy->setFrame(Rect{0, 0, 20, 20});
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
@@ -6465,8 +6469,8 @@
  */
 TEST_F(InputDispatcherSpyWindowTest, PilferPointers) {
     auto window = createForeground();
-    auto spy1 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
-    auto spy2 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy1 = createSpy();
+    auto spy2 = createSpy();
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window}}});
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6498,7 +6502,7 @@
  */
 TEST_F(InputDispatcherSpyWindowTest, CanPilferAfterWindowIsRemovedMidStream) {
     auto window = createForeground();
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy = createSpy();
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6522,9 +6526,8 @@
  * the spy, but not to any other windows.
  */
 TEST_F(InputDispatcherSpyWindowTest, ContinuesToReceiveGestureAfterPilfer) {
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
+    auto spy = createSpy();
     auto window = createForeground();
-    window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
 
@@ -6590,7 +6593,7 @@
     windowLeft->setFrame({0, 0, 100, 200});
     auto windowRight = createForeground();
     windowRight->setFrame({100, 0, 200, 200});
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spy = createSpy();
     spy->setFrame({0, 0, 200, 200});
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, windowLeft, windowRight}}});
 
@@ -6625,7 +6628,7 @@
 TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) {
     auto window = createForeground();
     window->setFrame({0, 0, 200, 200});
-    auto spyRight = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    auto spyRight = createSpy();
     spyRight->setFrame({100, 0, 200, 200});
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyRight, window}}});
 
@@ -6658,15 +6661,13 @@
  * windows should be allowed to control split touch.
  */
 TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) {
-    // Create a touch modal spy that spies on the entire display.
-    // This spy window does not set the SPLIT_TOUCH flag. However, we still expect to split touches
+    // This spy window prevents touch splitting. However, we still expect to split touches
     // because a foreground window has not disabled splitting.
-    auto spy = createSpy(static_cast<WindowInfo::Flag>(0));
+    auto spy = createSpy();
+    spy->setPreventSplitting(true);
 
-    // Create a non touch modal window that supports split touch.
     auto window = createForeground();
     window->setFrame(Rect(0, 0, 100, 100));
-    window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
 
@@ -6704,7 +6705,7 @@
  * do not receive key events.
  */
 TEST_F(InputDispatcherSpyWindowTest, UnfocusableSpyDoesNotReceiveKeyEvents) {
-    auto spy = createSpy(static_cast<WindowInfo::Flag>(0));
+    auto spy = createSpy();
     spy->setFocusable(false);
 
     auto window = createForeground();
@@ -6733,7 +6734,7 @@
                                      ADISPLAY_ID_DEFAULT);
         overlay->setFocusable(false);
         overlay->setOwnerInfo(111, 111);
-        overlay->setFlags(WindowInfo::Flag::NOT_TOUCHABLE | WindowInfo::Flag::SPLIT_TOUCH);
+        overlay->setTouchable(false);
         overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS);
         overlay->setTrustedOverlay(true);
 
@@ -6744,7 +6745,6 @@
                                      ADISPLAY_ID_DEFAULT);
         window->setFocusable(true);
         window->setOwnerInfo(222, 222);
-        window->setFlags(WindowInfo::Flag::SPLIT_TOUCH);
 
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
@@ -6820,7 +6820,4 @@
     window->assertNoEvents();
 }
 
-// TODO(b/198487159): Add permission tests for touch mode switch once the validation is put in
-//     place.
-
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 54cf15d..9f33d23 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -20,6 +20,7 @@
 #include <InputReader.h>
 #include <InputReaderBase.h>
 #include <InputReaderFactory.h>
+#include <JoystickInputMapper.h>
 #include <KeyboardInputMapper.h>
 #include <MultiTouchInputMapper.h>
 #include <PeripheralController.h>
@@ -8251,6 +8252,25 @@
     ASSERT_EQ(DISPLAY_ID, args.displayId);
 }
 
+TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayUniqueId) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
+
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+
+    // Send a touch event
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
+}
+
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
     // Setup for second display.
     std::shared_ptr<FakePointerController> fakePointerController =
@@ -9260,6 +9280,67 @@
     ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
+// --- JoystickInputMapperTest ---
+
+class JoystickInputMapperTest : public InputMapperTest {
+protected:
+    static const int32_t RAW_X_MIN;
+    static const int32_t RAW_X_MAX;
+    static const int32_t RAW_Y_MIN;
+    static const int32_t RAW_Y_MAX;
+
+    void SetUp() override {
+        InputMapperTest::SetUp(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL);
+    }
+    void prepareAxes() {
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+    }
+
+    void processAxis(JoystickInputMapper& mapper, int32_t axis, int32_t value) {
+        process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, axis, value);
+    }
+
+    void processSync(JoystickInputMapper& mapper) {
+        process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    }
+
+    void prepareVirtualDisplay(int32_t orientation) {
+        setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
+                                     VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID,
+                                     NO_PORT, ViewportType::VIRTUAL);
+    }
+};
+
+const int32_t JoystickInputMapperTest::RAW_X_MIN = -32767;
+const int32_t JoystickInputMapperTest::RAW_X_MAX = 32767;
+const int32_t JoystickInputMapperTest::RAW_Y_MIN = -32767;
+const int32_t JoystickInputMapperTest::RAW_Y_MAX = 32767;
+
+TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) {
+    prepareAxes();
+    JoystickInputMapper& mapper = addMapperAndConfigure<JoystickInputMapper>();
+
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
+
+    prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+
+    // Send an axis event
+    processAxis(mapper, ABS_X, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
+
+    // Send another axis event
+    processAxis(mapper, ABS_Y, 100);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
+}
+
 // --- PeripheralControllerTest ---
 
 class PeripheralControllerTest : public testing::Test {
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 132b877..9c93919 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -17,6 +17,7 @@
 #include "UinputDevice.h"
 
 #include <android-base/stringprintf.h>
+#include <fcntl.h>
 
 namespace android {
 
@@ -32,7 +33,7 @@
 }
 
 void UinputDevice::init() {
-    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK | O_CLOEXEC));
     if (mDeviceFd < 0) {
         FAIL() << "Can't open /dev/uinput :" << strerror(errno);
     }
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 971491d..3c164aa 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -134,6 +134,7 @@
       mWakeLockAcquired(false), mLastReportedProxIsActive(false) {
     mUidPolicy = new UidPolicy(this);
     mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
+    mMicSensorPrivacyPolicy = new MicrophonePrivacyPolicy(this);
 }
 
 bool SensorService::initializeHmacKey() {
@@ -369,6 +370,9 @@
 
             // Start watching sensor privacy changes
             mSensorPrivacyPolicy->registerSelf();
+
+            // Start watching mic sensor privacy changes
+            mMicSensorPrivacyPolicy->registerSelf();
         }
     }
 }
@@ -439,9 +443,7 @@
     }
     mUidPolicy->unregisterSelf();
     mSensorPrivacyPolicy->unregisterSelf();
-    for (auto const& [userId, policy] : mMicSensorPrivacyPolicies) {
-        policy->unregisterSelf();
-    }
+    mMicSensorPrivacyPolicy->unregisterSelf();
 }
 
 status_t SensorService::dump(int fd, const Vector<String16>& args) {
@@ -773,33 +775,25 @@
     checkAndReportProxStateChangeLocked();
 }
 
-void SensorService::capRates(userid_t userId) {
+void SensorService::capRates() {
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     for (const sp<SensorDirectConnection>& conn : connLock.getDirectConnections()) {
-        if (conn->getUserId() == userId) {
-            conn->onMicSensorAccessChanged(true);
-        }
+        conn->onMicSensorAccessChanged(true);
     }
 
     for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) {
-        if (conn->getUserId() == userId) {
-            conn->onMicSensorAccessChanged(true);
-        }
+        conn->onMicSensorAccessChanged(true);
     }
 }
 
-void SensorService::uncapRates(userid_t userId) {
+void SensorService::uncapRates() {
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     for (const sp<SensorDirectConnection>& conn : connLock.getDirectConnections()) {
-        if (conn->getUserId() == userId) {
-            conn->onMicSensorAccessChanged(false);
-        }
+        conn->onMicSensorAccessChanged(false);
     }
 
     for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) {
-        if (conn->getUserId() == userId) {
-            conn->onMicSensorAccessChanged(false);
-        }
+        conn->onMicSensorAccessChanged(false);
     }
 }
 
@@ -2243,7 +2237,6 @@
 
 status_t SensorService::adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs,
         const String16& opPackageName) {
-    uid_t uid = IPCThreadState::self()->getCallingUid();
     if (*requestedPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
         return OK;
     }
@@ -2255,7 +2248,7 @@
         }
         return OK;
     }
-    if (isMicSensorPrivacyEnabledForUid(uid)) {
+    if (mMicSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
         *requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
         return OK;
     }
@@ -2264,7 +2257,6 @@
 
 status_t SensorService::adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel,
         const String16& opPackageName) {
-    uid_t uid = IPCThreadState::self()->getCallingUid();
     if (*requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
         return OK;
     }
@@ -2276,7 +2268,7 @@
         }
         return OK;
     }
-    if (isMicSensorPrivacyEnabledForUid(uid)) {
+    if (mMicSensorPrivacyPolicy->isSensorPrivacyEnabled()) {
         *requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
         return OK;
     }
@@ -2293,69 +2285,63 @@
 void SensorService::SensorPrivacyPolicy::unregisterSelf() {
     AutoCallerClear acc;
     SensorPrivacyManager spm;
-    if (mIsIndividualMic) {
-        spm.removeIndividualSensorPrivacyListener(
-                SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE, this);
-    } else {
-        spm.removeSensorPrivacyListener(this);
-    }
+    spm.removeSensorPrivacyListener(this);
 }
 
 bool SensorService::SensorPrivacyPolicy::isSensorPrivacyEnabled() {
     return mSensorPrivacyEnabled;
 }
 
-binder::Status SensorService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool enabled) {
+binder::Status SensorService::SensorPrivacyPolicy::onSensorPrivacyChanged(int toggleType __unused,
+        int sensor __unused, bool enabled) {
     mSensorPrivacyEnabled = enabled;
     sp<SensorService> service = mService.promote();
 
     if (service != nullptr) {
-        if (mIsIndividualMic) {
-            if (enabled) {
-                service->capRates(mUserId);
-            } else {
-                service->uncapRates(mUserId);
-            }
+        if (enabled) {
+            service->disableAllSensors();
         } else {
-            if (enabled) {
-                service->disableAllSensors();
-            } else {
-                service->enableAllSensors();
-            }
+            service->enableAllSensors();
         }
     }
     return binder::Status::ok();
 }
 
-status_t SensorService::SensorPrivacyPolicy::registerSelfForIndividual(int userId) {
-    Mutex::Autolock _l(mSensorPrivacyLock);
+void SensorService::MicrophonePrivacyPolicy::registerSelf() {
     AutoCallerClear acc;
     SensorPrivacyManager spm;
-    status_t err = spm.addIndividualSensorPrivacyListener(userId,
-            SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE, this);
-
-    if (err != OK) {
-        ALOGE("Cannot register a mic listener.");
-        return err;
-    }
-    mSensorPrivacyEnabled = spm.isIndividualSensorPrivacyEnabled(userId,
-                SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE);
-
-    mIsIndividualMic = true;
-    mUserId = userId;
-    return OK;
+    mSensorPrivacyEnabled =
+            spm.isToggleSensorPrivacyEnabled(
+                    SensorPrivacyManager::TOGGLE_TYPE_SOFTWARE,
+            SensorPrivacyManager::TOGGLE_SENSOR_MICROPHONE)
+                    || spm.isToggleSensorPrivacyEnabled(
+                            SensorPrivacyManager::TOGGLE_TYPE_HARDWARE,
+                            SensorPrivacyManager::TOGGLE_SENSOR_MICROPHONE);
+    spm.addToggleSensorPrivacyListener(this);
 }
 
-bool SensorService::isMicSensorPrivacyEnabledForUid(uid_t uid) {
-    userid_t userId = multiuser_get_user_id(uid);
-    if (mMicSensorPrivacyPolicies.find(userId) == mMicSensorPrivacyPolicies.end()) {
-        sp<SensorPrivacyPolicy> userPolicy = new SensorPrivacyPolicy(this);
-        if (userPolicy->registerSelfForIndividual(userId) != OK) {
-            return false;
-        }
-        mMicSensorPrivacyPolicies[userId] = userPolicy;
+void SensorService::MicrophonePrivacyPolicy::unregisterSelf() {
+    AutoCallerClear acc;
+    SensorPrivacyManager spm;
+    spm.removeToggleSensorPrivacyListener(this);
+}
+
+binder::Status SensorService::MicrophonePrivacyPolicy::onSensorPrivacyChanged(int toggleType __unused,
+        int sensor, bool enabled) {
+    if (sensor != SensorPrivacyManager::TOGGLE_SENSOR_MICROPHONE) {
+        return binder::Status::ok();
     }
-    return mMicSensorPrivacyPolicies[userId]->isSensorPrivacyEnabled();
+    mSensorPrivacyEnabled = enabled;
+    sp<SensorService> service = mService.promote();
+
+    if (service != nullptr) {
+        if (enabled) {
+            service->capRates();
+        } else {
+            service->uncapRates();
+        }
+    }
+    return binder::Status::ok();
 }
 
 SensorService::ConnectionSafeAutolock::ConnectionSafeAutolock(
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index b009829..7194db3 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -289,22 +289,32 @@
     class SensorPrivacyPolicy : public hardware::BnSensorPrivacyListener {
         public:
             explicit SensorPrivacyPolicy(wp<SensorService> service)
-                    : mService(service), mIsIndividualMic(false), mUserId(0) {}
+                    : mService(service) {}
             void registerSelf();
             void unregisterSelf();
 
-            status_t registerSelfForIndividual(int userId);
-
             bool isSensorPrivacyEnabled();
 
-            binder::Status onSensorPrivacyChanged(bool enabled);
+            binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
+                                                  bool enabled);
+
+        protected:
+            std::atomic_bool mSensorPrivacyEnabled;
+            wp<SensorService> mService;
 
         private:
-            wp<SensorService> mService;
             Mutex mSensorPrivacyLock;
-            std::atomic_bool mSensorPrivacyEnabled;
-            bool mIsIndividualMic;
-            userid_t mUserId;
+    };
+
+    class MicrophonePrivacyPolicy : public SensorPrivacyPolicy {
+        public:
+            explicit MicrophonePrivacyPolicy(wp<SensorService> service)
+                    : SensorPrivacyPolicy(service) {}
+            void registerSelf();
+            void unregisterSelf();
+
+            binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
+                                                  bool enabled);
     };
 
     // A class automatically clearing and restoring binder caller identity inside
@@ -444,9 +454,9 @@
     void enableAllSensorsLocked(ConnectionSafeAutolock* connLock);
 
     // Caps active direct connections (when the mic toggle is flipped to on)
-    void capRates(userid_t userId);
+    void capRates();
     // Removes the capped rate on active direct connections (when the mic toggle is flipped to off)
-    void uncapRates(userid_t userId);
+    void uncapRates();
 
     static inline bool isAudioServerOrSystemServerUid(uid_t uid) {
         return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER;
@@ -497,10 +507,7 @@
     static Mutex sPackageTargetVersionLock;
     static String16 sSensorInterfaceDescriptorPrefix;
 
-    // Map from user to SensorPrivacyPolicy
-    std::map<userid_t, sp<SensorPrivacyPolicy>> mMicSensorPrivacyPolicies;
-    // Checks if the mic sensor privacy is enabled for the uid
-    bool isMicSensorPrivacyEnabledForUid(uid_t uid);
+    sp<MicrophonePrivacyPolicy> mMicSensorPrivacyPolicy;
 
     // Keeps track of the handles of all proximity sensors in the system.
     std::vector<int32_t> mProxSensorHandles;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 40fc342..02e444d 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -416,8 +416,14 @@
             ? bufferData.acquireFence
             : Fence::NO_FENCE;
     mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
-    // The acquire fences of BufferStateLayers have already signaled before they are set
-    mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime();
+    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);
@@ -527,7 +533,7 @@
         // 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->acquireTime = mCallbackHandleAcquireTime;
+            handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
             handle->frameNumber = mDrawingState.frameNumber;
 
             // Store so latched time and release fence can be set
@@ -540,7 +546,7 @@
     }
 
     mReleasePreviousBuffer = false;
-    mCallbackHandleAcquireTime = -1;
+    mCallbackHandleAcquireTimeOrFence = -1;
 
     return willPresent;
 }
@@ -918,4 +924,183 @@
     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;
+        }
+    }
+
+    ALOGV("%s: true", __func__);
+    return true;
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 248e013..669eaad 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -136,6 +136,8 @@
 
     bool bufferNeedsFiltering() const override;
 
+    bool simpleBufferUpdate(const layer_state_t& s) const override;
+
     ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
@@ -143,7 +145,7 @@
 
     // Stores the last set acquire fence signal time used to populate the callback handle's acquire
     // time.
-    nsecs_t mCallbackHandleAcquireTime = -1;
+    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.
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 63c7f63..ff332eb 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1080,9 +1080,9 @@
             outputState.clientTargetBrightness * outputState.displayBrightnessNits;
 
     // Compute the global color transform matrix.
-    if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
-        clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
-    }
+    clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
+    clientCompositionDisplay.deviceHandlesColorTransform =
+            outputState.usesDeviceComposition || getSkipColorTransform();
 
     // Generate the client composition requests for the layers on this output.
     std::vector<LayerFE*> clientCompositionLayersFE;
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 8ec8f00..719f15c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -129,6 +129,10 @@
                 (const, override));
     MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
                 (const, override));
+    MOCK_METHOD2(getDisplayDecorationSupport,
+                 status_t(PhysicalDisplayId,
+                          std::optional<aidl::android::hardware::graphics::common::
+                                                DisplayDecorationSupport>* support));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 53c587a..c2521b2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3488,7 +3488,8 @@
                                             .maxLuminance = kDefaultMaxLuminance,
                                             .currentLuminanceNits = kDefaultMaxLuminance,
                                             .outputDataspace = kDefaultOutputDataspace,
-                                            .colorTransform = mat4(),
+                                            .colorTransform = kDefaultColorTransformMat,
+                                            .deviceHandlesColorTransform = true,
                                             .orientation = kDefaultOutputOrientationFlags,
                                             .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
@@ -3506,7 +3507,8 @@
                                             .maxLuminance = kDefaultMaxLuminance,
                                             .currentLuminanceNits = kDisplayLuminance,
                                             .outputDataspace = kDefaultOutputDataspace,
-                                            .colorTransform = mat4(),
+                                            .colorTransform = kDefaultColorTransformMat,
+                                            .deviceHandlesColorTransform = true,
                                             .orientation = kDefaultOutputOrientationFlags,
                                             .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
@@ -3523,7 +3525,8 @@
                                             .maxLuminance = kDefaultMaxLuminance,
                                             .currentLuminanceNits = kDefaultMaxLuminance,
                                             .outputDataspace = kDefaultOutputDataspace,
-                                            .colorTransform = mat4(),
+                                            .colorTransform = kDefaultColorTransformMat,
+                                            .deviceHandlesColorTransform = true,
                                             .orientation = kDefaultOutputOrientationFlags,
                                             .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
@@ -3541,6 +3544,7 @@
                                             .currentLuminanceNits = kDefaultMaxLuminance,
                                             .outputDataspace = kDefaultOutputDataspace,
                                             .colorTransform = kDefaultColorTransformMat,
+                                            .deviceHandlesColorTransform = false,
                                             .orientation = kDefaultOutputOrientationFlags,
                                             .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
@@ -3558,6 +3562,7 @@
                                             .currentLuminanceNits = kDefaultMaxLuminance,
                                             .outputDataspace = kDefaultOutputDataspace,
                                             .colorTransform = kDefaultColorTransformMat,
+                                            .deviceHandlesColorTransform = false,
                                             .orientation = kDefaultOutputOrientationFlags,
                                             .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
@@ -3575,7 +3580,8 @@
                                             .maxLuminance = kDefaultMaxLuminance,
                                             .currentLuminanceNits = kDefaultMaxLuminance,
                                             .outputDataspace = kDefaultOutputDataspace,
-                                            .colorTransform = mat4(),
+                                            .colorTransform = kDefaultColorTransformMat,
+                                            .deviceHandlesColorTransform = true,
                                             .orientation = kDefaultOutputOrientationFlags,
                                             .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f542161..eef0052 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -141,7 +141,7 @@
     return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
 }
 
-std::pair<gui::DisplayInfo, ui::Transform> DisplayDevice::getInputInfo() const {
+auto DisplayDevice::getInputInfo() const -> InputInfo {
     gui::DisplayInfo info;
     info.displayId = getLayerStack().id;
 
@@ -166,10 +166,13 @@
 
     info.logicalWidth = getLayerStackSpaceRect().width();
     info.logicalHeight = getLayerStackSpaceRect().height();
-    return {info, displayTransform};
+
+    return {.info = info,
+            .transform = displayTransform,
+            .receivesInput = receivesInput(),
+            .isSecure = isSecure()};
 }
 
-// ----------------------------------------------------------------------------
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
     mPowerMode = mode;
     getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3cae30f..bf6b31a 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -174,9 +174,14 @@
         return mDeviceProductInfo;
     }
 
-    // Get the DisplayInfo that will be sent to InputFlinger, and the display transform that should
-    // be applied to all the input windows on the display.
-    std::pair<gui::DisplayInfo, ui::Transform> getInputInfo() const;
+    struct InputInfo {
+        gui::DisplayInfo info;
+        ui::Transform transform;
+        bool receivesInput;
+        bool isSecure;
+    };
+
+    InputInfo getInputInfo() const;
 
     /* ------------------------------------------------------------------------
      * Display power mode management.
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 64cb497..80e2d99 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -20,6 +20,7 @@
 
 #include "AidlComposerHal.h"
 
+#include <android-base/file.h>
 #include <android/binder_ibinder_platform.h>
 #include <android/binder_manager.h>
 #include <log/log.h>
@@ -115,7 +116,7 @@
 AidlPerFrameMetadataBlob translate(IComposerClient::PerFrameMetadataBlob x) {
     AidlPerFrameMetadataBlob blob;
     blob.key = translate<AidlPerFrameMetadataKey>(x.key),
-    std::copy(blob.blob.begin(), blob.blob.end(), x.blob.begin());
+    std::copy(x.blob.begin(), x.blob.end(), std::inserter(blob.blob, blob.blob.end()));
     return blob;
 }
 
@@ -262,13 +263,27 @@
 }
 
 std::string AidlComposer::dumpDebugInfo() {
-    std::string info;
-    const auto status = mAidlComposer->dumpDebugInfo(&info);
-    if (!status.isOk()) {
-        ALOGE("dumpDebugInfo failed %s", status.getDescription().c_str());
+    int pipefds[2];
+    int result = pipe(pipefds);
+    if (result < 0) {
+        ALOGE("dumpDebugInfo: pipe failed: %s", strerror(errno));
         return {};
     }
-    return info;
+
+    std::string 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 {
+        ALOGE("dumpDebugInfo: dump failed: %d", status);
+    }
+
+    close(pipefds[0]);
+    return str;
 }
 
 void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) {
@@ -1073,5 +1088,17 @@
                                    translate<AidlRect>(blocking));
     return Error::NONE;
 }
+
+Error AidlComposer::getDisplayDecorationSupport(Display display,
+                                                std::optional<DisplayDecorationSupport>* support) {
+    const auto status =
+            mAidlComposerClient->getDisplayDecorationSupport(translate<int64_t>(display), support);
+    if (!status.isOk()) {
+        ALOGE("getDisplayDecorationSupport failed %s", status.getDescription().c_str());
+        support->reset();
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
 } // namespace Hwc2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 2ad119c..80ca8da 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -45,6 +45,7 @@
 
 namespace android::Hwc2 {
 
+using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
 using aidl::android::hardware::graphics::composer3::ComposerClientReader;
 using aidl::android::hardware::graphics::composer3::ComposerClientWriter;
 
@@ -217,6 +218,8 @@
     Error setBootDisplayConfig(Display displayId, Config) override;
     Error clearBootDisplayConfig(Display displayId) override;
     Error getPreferredBootDisplayConfig(Display displayId, Config*) override;
+    Error getDisplayDecorationSupport(Display display,
+                                      std::optional<DisplayDecorationSupport>* support) override;
 
 private:
     // Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index fd6f6a4..23886d4 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -31,12 +31,15 @@
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
 
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
 
+#include <optional>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
@@ -267,6 +270,10 @@
     virtual Error setBootDisplayConfig(Display displayId, Config) = 0;
     virtual Error clearBootDisplayConfig(Display displayId) = 0;
     virtual Error getPreferredBootDisplayConfig(Display displayId, Config*) = 0;
+    virtual Error getDisplayDecorationSupport(
+            Display display,
+            std::optional<::aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                    support) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 73f9468..a1b663c 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -581,6 +581,13 @@
     return static_cast<Error>(error);
 }
 
+Error Display::getDisplayDecorationSupport(
+        std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                support) {
+    const auto error = mComposer.getDisplayDecorationSupport(mId, support);
+    return static_cast<Error>(error);
+}
+
 // For use by Device
 
 void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index a37b8dd..334d6ec 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -37,6 +37,7 @@
 #include "ComposerHal.h"
 #include "Hal.h"
 
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
@@ -162,6 +163,9 @@
     [[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
     [[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
             hal::ClientTargetProperty* outClientTargetProperty, float* outWhitePointNits) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getDisplayDecorationSupport(
+            std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                    support) = 0;
 };
 
 namespace impl {
@@ -235,6 +239,9 @@
     hal::Error setContentType(hal::ContentType) override;
     hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty,
                                        float* outWhitePointNits) override;
+    hal::Error getDisplayDecorationSupport(
+            std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                    support) override;
 
     // Other Display methods
     hal::HWDisplayId getId() const override { return mId; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 956eca2..ed6e4b0 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -784,6 +784,22 @@
     return displayModeId;
 }
 
+status_t HWComposer::getDisplayDecorationSupport(
+        PhysicalDisplayId displayId,
+        std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                support) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->getDisplayDecorationSupport(support);
+    if (error == hal::Error::UNSUPPORTED) {
+        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    }
+    if (error == hal::Error::BAD_PARAMETER) {
+        RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+    }
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
+}
+
 status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 7c61bf1..5b2e265 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -43,6 +43,7 @@
 #include "HWC2.h"
 #include "Hal.h"
 
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
@@ -262,6 +263,10 @@
     virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0;
     virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0;
     virtual std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) = 0;
+    virtual status_t getDisplayDecorationSupport(
+            PhysicalDisplayId,
+            std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                    support) = 0;
 };
 
 namespace impl {
@@ -393,6 +398,10 @@
     status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) override;
     status_t clearBootDisplayMode(PhysicalDisplayId) override;
     std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) override;
+    status_t getDisplayDecorationSupport(
+            PhysicalDisplayId,
+            std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                    support) override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 40c9761..4737034 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -135,8 +135,8 @@
             return "AutoLowLatencyMode";
         case aidl::android::hardware::graphics::composer3::DisplayCapability::SUSPEND:
             return "Suspend";
-        case aidl::android::hardware::graphics::composer3::DisplayCapability::DISPLAY_DECORATION:
-            return "DisplayDecoration";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::DISPLAY_IDLE_TIMER:
+            return "DisplayIdleTimer";
         default:
             return "Unknown";
     }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 8680a53..f735bba 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1312,6 +1312,14 @@
     return Error::NONE;
 }
 
+Error HidlComposer::getDisplayDecorationSupport(
+        Display,
+        std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                support) {
+    support->reset();
+    return Error::UNSUPPORTED;
+}
+
 void HidlComposer::registerCallback(ComposerCallback& callback) {
     const bool vsyncSwitchingSupported =
             isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 32fe748..c2b60cb 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -327,6 +327,10 @@
     Error setBootDisplayConfig(Display displayId, Config) override;
     Error clearBootDisplayConfig(Display displayId) override;
     Error getPreferredBootDisplayConfig(Display displayId, Config*) override;
+    Error getDisplayDecorationSupport(
+            Display display,
+            std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                    support) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
index e09a192..bd3cf74 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -101,7 +101,6 @@
 }
 
 bool FlagManager::use_skia_tracing() const {
-    ALOGD("use_skia_tracing ?");
     std::optional<bool> sysPropVal =
             doParse<bool>(base::GetProperty(PROPERTY_SKIA_ATRACE_ENABLED, "").c_str());
     return getValue("SkiaTracingFeature__use_skia_tracing", sysPropVal, false);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 86e96d7..81747d5 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -492,17 +492,22 @@
 
 void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
                                       nsecs_t& deadlineDelta) {
-    if (mPredictionState == PredictionState::Expired ||
-        mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+    if (mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
         // Cannot do any classification for invalid present time.
-        // For prediction expired case, we do not know what happened here to classify this
-        // correctly. This could potentially be AppDeadlineMissed but that's assuming no app will
-        // request frames 120ms apart.
         mJankType = JankType::Unknown;
         deadlineDelta = -1;
         return;
     }
 
+    if (mPredictionState == PredictionState::Expired) {
+        // We classify prediction expired as AppDeadlineMissed as the
+        // TokenManager::kMaxTokens we store is large enough to account for a
+        // reasonable app, so prediction expire would mean a huge scheduling delay.
+        mJankType = JankType::AppDeadlineMissed;
+        deadlineDelta = -1;
+        return;
+    }
+
     if (mPredictionState == PredictionState::None) {
         // Cannot do jank classification on frames that don't have a token.
         return;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a039250..8081096 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1993,29 +1993,31 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
-LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
-                                const DisplayDevice* display) {
+LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) {
     LayerProto* layerProto = layersProto.add_layers();
-    writeToProtoDrawingState(layerProto, traceFlags, display);
+    writeToProtoDrawingState(layerProto);
     writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
 
     if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
         // Only populate for the primary display.
+        UnnecessaryLock assumeLocked(mFlinger->mStateLock); // called from the main thread.
+        const auto display = mFlinger->getDefaultDisplayDeviceLocked();
         if (display) {
             const auto compositionType = getCompositionType(*display);
             layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
+            LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
+                                           [&]() { return layerProto->mutable_visible_region(); });
         }
     }
 
     for (const sp<Layer>& layer : mDrawingChildren) {
-        layer->writeToProto(layersProto, traceFlags, display);
+        layer->writeToProto(layersProto, traceFlags);
     }
 
     return layerProto;
 }
 
-void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
-                                     const DisplayDevice* display) {
+void Layer::writeToProtoDrawingState(LayerProto* layerInfo) {
     const ui::Transform transform = getTransform();
     auto buffer = getExternalTexture();
     if (buffer != nullptr) {
@@ -2039,10 +2041,6 @@
     LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
                                            [&]() { return layerInfo->mutable_position(); });
     LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
-    if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
-        LayerProtoHelper::writeToProto(getVisibleRegion(display),
-                                       [&]() { return layerInfo->mutable_visible_region(); });
-    }
     LayerProtoHelper::writeToProto(surfaceDamageRegion,
                                    [&]() { return layerInfo->mutable_damage_region(); });
 
@@ -2161,8 +2159,7 @@
 void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
     Rect tmpBounds = getInputBounds();
     if (!tmpBounds.isValid()) {
-        info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE;
-        info.focusable = false;
+        info.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, true);
         info.touchableRegion.clear();
         // A layer could have invalid input bounds and still expect to receive touch input if it has
         // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated
@@ -2309,7 +2306,6 @@
         mDrawingState.inputInfo.ownerUid = mOwnerUid;
         mDrawingState.inputInfo.ownerPid = mOwnerPid;
         mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
-        mDrawingState.inputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL;
         mDrawingState.inputInfo.displayId = getLayerStack().id;
     }
 
@@ -2327,7 +2323,9 @@
     // We are just using these layers for occlusion detection in
     // InputDispatcher, and obviously if they aren't visible they can't occlude
     // anything.
-    info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
+    const bool visible = hasInputInfo() ? canReceiveInput() : isVisible();
+    info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+
     info.alpha = getAlpha();
     fillTouchOcclusionMode(info);
     handleDropInputMode(info);
@@ -2349,8 +2347,9 @@
 
     // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
     // if it was set by WM for a known system overlay
-    info.trustedOverlay = info.trustedOverlay || isTrustedOverlay();
-
+    if (isTrustedOverlay()) {
+        info.inputConfig |= WindowInfo::InputConfig::TRUSTED_OVERLAY;
+    }
 
     // If the layer is a clone, we need to crop the input region to cloned root to prevent
     // touches from going outside the cloned area.
@@ -2482,7 +2481,7 @@
     }
     // Cloned layers shouldn't handle watch outside since their z order is not determined by
     // WM or the client.
-    mDrawingState.inputInfo.flags &= ~WindowInfo::Flag::WATCH_OUTSIDE_TOUCH;
+    mDrawingState.inputInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, false);
 }
 
 void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ddcd641..21dd5f4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -686,12 +686,12 @@
 
     bool isRemovedFromCurrentState() const;
 
-    LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
+    LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags);
 
     // Write states that are modified by the main thread. This includes drawing
     // state as well as buffer data. This should be called in the main or tracing
     // thread.
-    void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, const DisplayDevice*);
+    void writeToProtoDrawingState(LayerProto* layerInfo);
     // Write drawing or current state. If writing current state, the caller should hold the
     // external mStateLock. If writing drawing state, this function should be called on the
     // main or tracing thread.
@@ -897,6 +897,8 @@
     virtual std::string getPendingBufferCounterName() { return ""; }
     virtual bool updateGeometry() { return false; }
 
+    virtual bool simpleBufferUpdate(const layer_state_t&) const { return false; }
+
 protected:
     friend class impl::SurfaceInterceptor;
 
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 015caa6..0506c47 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -156,14 +156,14 @@
 
 void LayerProtoHelper::writeToProto(const renderengine::ExternalTexture& buffer,
                                     std::function<ActiveBufferProto*()> getActiveBufferProto) {
-    if (buffer.getBuffer()->getWidth() != 0 || buffer.getBuffer()->getHeight() != 0 ||
-        buffer.getBuffer()->getUsage() != 0 || buffer.getBuffer()->getPixelFormat() != 0) {
+    if (buffer.getWidth() != 0 || buffer.getHeight() != 0 || buffer.getUsage() != 0 ||
+        buffer.getPixelFormat() != 0) {
         // Use a lambda do avoid writing the object header when the object is empty
         ActiveBufferProto* activeBufferProto = getActiveBufferProto();
-        activeBufferProto->set_width(buffer.getBuffer()->getWidth());
-        activeBufferProto->set_height(buffer.getBuffer()->getHeight());
-        activeBufferProto->set_format(buffer.getBuffer()->getPixelFormat());
-        activeBufferProto->set_usage(buffer.getBuffer()->getUsage());
+        activeBufferProto->set_width(buffer.getWidth());
+        activeBufferProto->set_height(buffer.getHeight());
+        activeBufferProto->set_stride(buffer.getUsage());
+        activeBufferProto->set_format(buffer.getPixelFormat());
     }
 }
 
@@ -175,12 +175,12 @@
     }
 
     InputWindowInfoProto* proto = getInputWindowInfoProto();
-    proto->set_layout_params_flags(inputInfo.flags.get());
+    proto->set_layout_params_flags(inputInfo.layoutParamsFlags.get());
     using U = std::underlying_type_t<WindowInfo::Type>;
     // TODO(b/129481165): This static assert can be safely removed once conversion warnings
     // are re-enabled.
     static_assert(std::is_same_v<U, int32_t>);
-    proto->set_layout_params_type(static_cast<U>(inputInfo.type));
+    proto->set_layout_params_type(static_cast<U>(inputInfo.layoutParamsType));
 
     LayerProtoHelper::writeToProto({inputInfo.frameLeft, inputInfo.frameTop, inputInfo.frameRight,
                                     inputInfo.frameBottom},
@@ -189,9 +189,10 @@
                                    [&]() { return proto->mutable_touchable_region(); });
 
     proto->set_surface_inset(inputInfo.surfaceInset);
-    proto->set_visible(inputInfo.visible);
-    proto->set_focusable(inputInfo.focusable);
-    proto->set_has_wallpaper(inputInfo.hasWallpaper);
+    using InputConfig = gui::WindowInfo::InputConfig;
+    proto->set_visible(!inputInfo.inputConfig.test(InputConfig::NOT_VISIBLE));
+    proto->set_focusable(!inputInfo.inputConfig.test(InputConfig::NOT_FOCUSABLE));
+    proto->set_has_wallpaper(inputInfo.inputConfig.test(InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
 
     proto->set_global_scale_factor(inputInfo.globalScaleFactor);
     LayerProtoHelper::writeToProtoDeprecated(inputInfo.transform, proto->mutable_transform());
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 2d0da46..5ba8a1b 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -87,9 +87,10 @@
                                 to_string(event.header.displayId).c_str(),
                                 event.hotplug.connected ? "connected" : "disconnected");
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
-            return StringPrintf("VSync{displayId=%s, count=%u, expectedVSyncTimestamp=%" PRId64 "}",
+            return StringPrintf("VSync{displayId=%s, count=%u, expectedPresentationTime=%" PRId64
+                                "}",
                                 to_string(event.header.displayId).c_str(), event.vsync.count,
-                                event.vsync.expectedVSyncTimestamp);
+                                event.vsync.vsyncData.preferredExpectedPresentationTime());
         case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
             return StringPrintf("ModeChanged{displayId=%s, modeId=%u}",
                                 to_string(event.header.displayId).c_str(), event.modeChange.modeId);
@@ -107,13 +108,19 @@
 }
 
 DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
-                                      uint32_t count, nsecs_t expectedVSyncTimestamp,
+                                      uint32_t count, nsecs_t expectedPresentationTime,
                                       nsecs_t deadlineTimestamp) {
     DisplayEventReceiver::Event event;
     event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
     event.vsync.count = count;
-    event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp;
-    event.vsync.deadlineTimestamp = deadlineTimestamp;
+    event.vsync.vsyncData.preferredFrameTimelineIndex = 0;
+    // Temporarily store the current vsync information in frameTimelines[0], marked as
+    // platform-preferred. When the event is dispatched later, the frame interval at that time is
+    // used with this information to generate multiple frame timeline choices.
+    event.vsync.vsyncData.frameTimelines[0] = {.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID,
+                                               .deadlineTimestamp = deadlineTimestamp,
+                                               .expectedPresentationTime =
+                                                       expectedPresentationTime};
     return event;
 }
 
@@ -186,9 +193,10 @@
     return binder::Status::ok();
 }
 
-binder::Status EventThreadConnection::getLatestVsyncEventData(VsyncEventData* outVsyncEventData) {
+binder::Status EventThreadConnection::getLatestVsyncEventData(
+        ParcelableVsyncEventData* outVsyncEventData) {
     ATRACE_CALL();
-    *outVsyncEventData = mEventThread->getLatestVsyncEventData(this);
+    outVsyncEventData->vsync = mEventThread->getLatestVsyncEventData(this);
     return binder::Status::ok();
 }
 
@@ -338,10 +346,16 @@
 
 VsyncEventData EventThread::getLatestVsyncEventData(
         const sp<EventThreadConnection>& connection) const {
-    nsecs_t frameInterval = mGetVsyncPeriodFunction(connection->mOwnerUid);
     VsyncEventData vsyncEventData;
+    nsecs_t frameInterval = mGetVsyncPeriodFunction(connection->mOwnerUid);
     vsyncEventData.frameInterval = frameInterval;
-    generateFrameTimeline(vsyncEventData, frameInterval, systemTime(SYSTEM_TIME_MONOTONIC));
+    VSyncSource::VSyncData vsyncData;
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        vsyncData = mVSyncSource->getLatestVSyncData();
+    }
+    generateFrameTimeline(vsyncEventData, frameInterval, systemTime(SYSTEM_TIME_MONOTONIC),
+                          vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp);
     return vsyncEventData;
 }
 
@@ -370,7 +384,7 @@
 
     LOG_FATAL_IF(!mVSyncState);
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
-                                       vsyncData.expectedVSyncTimestamp,
+                                       vsyncData.expectedPresentationTime,
                                        vsyncData.deadlineTimestamp));
     mCondition.notify_all();
 }
@@ -518,7 +532,8 @@
                                      const sp<EventThreadConnection>& connection) const {
     const auto throttleVsync = [&] {
         return mThrottleVsyncCallback &&
-                mThrottleVsyncCallback(event.vsync.expectedVSyncTimestamp, connection->mOwnerUid);
+                mThrottleVsyncCallback(event.vsync.vsyncData.preferredExpectedPresentationTime(),
+                                       connection->mOwnerUid);
     };
 
     switch (event.header.type) {
@@ -568,79 +583,49 @@
 }
 
 int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
-                                   nsecs_t expectedVSyncTimestamp) const {
+                                   nsecs_t expectedPresentationTime) const {
     if (mTokenManager != nullptr) {
         return mTokenManager->generateTokenForPredictions(
-                {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+                {timestamp, deadlineTimestamp, expectedPresentationTime});
     }
     return FrameTimelineInfo::INVALID_VSYNC_ID;
 }
 
-void EventThread::generateFrameTimeline(
-        nsecs_t frameInterval, nsecs_t timestamp, nsecs_t preferredExpectedVSyncTimestamp,
-        nsecs_t preferredDeadlineTimestamp,
-        std::function<void(int64_t index)> setPreferredFrameTimelineIndex,
-        std::function<void(int64_t index, int64_t vsyncId, nsecs_t expectedVSyncTimestamp,
-                           nsecs_t deadlineTimestamp)>
-                setFrameTimeline) const {
+void EventThread::generateFrameTimeline(VsyncEventData& outVsyncEventData, nsecs_t frameInterval,
+                                        nsecs_t timestamp,
+                                        nsecs_t preferredExpectedPresentationTime,
+                                        nsecs_t preferredDeadlineTimestamp) const {
     // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
     for (int64_t multiplier = -VsyncEventData::kFrameTimelinesLength + 1, currentIndex = 0;
          currentIndex < VsyncEventData::kFrameTimelinesLength; multiplier++) {
-        nsecs_t deadline = preferredDeadlineTimestamp + multiplier * frameInterval;
+        nsecs_t deadlineTimestamp = preferredDeadlineTimestamp + multiplier * frameInterval;
         // Valid possible frame timelines must have future values.
-        if (deadline > timestamp) {
+        if (deadlineTimestamp > timestamp) {
             if (multiplier == 0) {
-                setPreferredFrameTimelineIndex(currentIndex);
+                outVsyncEventData.preferredFrameTimelineIndex = currentIndex;
             }
-            nsecs_t expectedVSyncTimestamp =
-                    preferredExpectedVSyncTimestamp + multiplier * frameInterval;
-            setFrameTimeline(currentIndex,
-                             generateToken(timestamp, deadline, expectedVSyncTimestamp),
-                             expectedVSyncTimestamp, deadline);
+            nsecs_t expectedPresentationTime =
+                    preferredExpectedPresentationTime + multiplier * frameInterval;
+            outVsyncEventData.frameTimelines[currentIndex] =
+                    {.vsyncId =
+                             generateToken(timestamp, deadlineTimestamp, expectedPresentationTime),
+                     .deadlineTimestamp = deadlineTimestamp,
+                     .expectedPresentationTime = expectedPresentationTime};
             currentIndex++;
         }
     }
 }
 
-void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) const {
-    generateFrameTimeline(
-            event.vsync.frameInterval, event.header.timestamp, event.vsync.expectedVSyncTimestamp,
-            event.vsync.deadlineTimestamp,
-            [&](int index) { event.vsync.preferredFrameTimelineIndex = index; },
-            [&](int64_t index, int64_t vsyncId, nsecs_t expectedVSyncTimestamp,
-                nsecs_t deadlineTimestamp) {
-                event.vsync.frameTimelines[index] = {.vsyncId = vsyncId,
-                                                     .deadlineTimestamp = deadlineTimestamp,
-                                                     .expectedVSyncTimestamp =
-                                                             expectedVSyncTimestamp};
-            });
-}
-
-void EventThread::generateFrameTimeline(VsyncEventData& out, const nsecs_t frameInterval,
-                                        const nsecs_t timestamp) const {
-    VSyncSource::VSyncData vsyncData;
-    {
-        std::lock_guard<std::mutex> lock(mMutex);
-        vsyncData = mVSyncSource->getLatestVSyncData();
-    }
-    generateFrameTimeline(
-            frameInterval, timestamp, vsyncData.expectedVSyncTimestamp, vsyncData.deadlineTimestamp,
-            [&](int index) { out.preferredFrameTimelineIndex = index; },
-            [&](int64_t index, int64_t vsyncId, nsecs_t expectedVSyncTimestamp,
-                nsecs_t deadlineTimestamp) {
-                out.frameTimelines[index] =
-                        VsyncEventData::FrameTimeline(vsyncId, deadlineTimestamp,
-                                                      expectedVSyncTimestamp);
-            });
-}
-
 void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
                                 const DisplayEventConsumers& consumers) {
     for (const auto& consumer : consumers) {
         DisplayEventReceiver::Event copy = event;
         if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-            copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
-            generateFrameTimeline(copy);
+            const int64_t frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
+            copy.vsync.vsyncData.frameInterval = frameInterval;
+            generateFrameTimeline(copy.vsync.vsyncData, frameInterval, copy.header.timestamp,
+                                  event.vsync.vsyncData.preferredExpectedPresentationTime(),
+                                  event.vsync.vsyncData.preferredDeadlineTimestamp());
         }
         switch (consumer->postEvent(copy)) {
             case NO_ERROR:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a858169..c406478 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -45,6 +45,7 @@
 class TokenManager;
 } // namespace frametimeline
 
+using gui::ParcelableVsyncEventData;
 using gui::VsyncEventData;
 
 // ---------------------------------------------------------------------------
@@ -66,7 +67,7 @@
 public:
     class VSyncData {
     public:
-        nsecs_t expectedVSyncTimestamp;
+        nsecs_t expectedPresentationTime;
         nsecs_t deadlineTimestamp;
     };
 
@@ -99,7 +100,7 @@
     binder::Status stealReceiveChannel(gui::BitTube* outChannel) override;
     binder::Status setVsyncRate(int rate) override;
     binder::Status requestNextVsync() override; // asynchronous
-    binder::Status getLatestVsyncEventData(VsyncEventData* outVsyncEventData) override;
+    binder::Status getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) override;
 
     // Called in response to requestNextVsync.
     const ResyncCallback resyncCallback;
@@ -217,17 +218,10 @@
     void onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) override;
 
     int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
-                          nsecs_t expectedVSyncTimestamp) const;
-    void generateFrameTimeline(DisplayEventReceiver::Event& event) const;
-    void generateFrameTimeline(VsyncEventData& out, const nsecs_t frameInterval,
-                               const nsecs_t timestamp) const;
-    void generateFrameTimeline(
-            nsecs_t frameInterval, nsecs_t timestamp, nsecs_t preferredExpectedVSyncTimestamp,
-            nsecs_t preferredDeadlineTimestamp,
-            std::function<void(int64_t index)> setPreferredFrameTimelineIndex,
-            std::function<void(int64_t index, int64_t vsyncId, nsecs_t expectedVSyncTimestamp,
-                               nsecs_t deadlineTimestamp)>
-                    setFrameTimeline) const;
+                          nsecs_t expectedPresentationTime) const;
+    void generateFrameTimeline(VsyncEventData& outVsyncEventData, nsecs_t frameInterval,
+                               nsecs_t timestamp, nsecs_t preferredExpectedPresentationTime,
+                               nsecs_t preferredDeadlineTimestamp) const;
 
     const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
     frametimeline::TokenManager* const mTokenManager;
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
index d9d64ae..c233455 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
@@ -51,7 +51,8 @@
     return std::nullopt;
 }
 
-std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides() {
+std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides(
+        bool supportsFrameRateOverrideByContent) {
     std::lock_guard lock(mFrameRateOverridesLock);
     std::vector<FrameRateOverride> overrides;
     overrides.reserve(std::max({mFrameRateOverridesFromGameManager.size(),
@@ -67,6 +68,11 @@
             overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
         }
     }
+
+    if (!supportsFrameRateOverrideByContent) {
+        return overrides;
+    }
+
     for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
         if (std::find_if(overrides.begin(), overrides.end(),
                          [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
index 278f87c..4185a4c 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
@@ -32,7 +32,8 @@
     std::optional<Fps> getFrameRateOverrideForUid(uid_t uid,
                                                   bool supportsFrameRateOverrideByContent) const
             EXCLUDES(mFrameRateOverridesLock);
-    std::vector<FrameRateOverride> getAllFrameRateOverrides() EXCLUDES(mFrameRateOverridesLock);
+    std::vector<FrameRateOverride> getAllFrameRateOverrides(bool supportsFrameRateOverrideByContent)
+            EXCLUDES(mFrameRateOverridesLock);
     void dump(std::string& result) const;
     bool updateFrameRateOverridesByContent(const UidToFrameRateOverride& frameRateOverrides)
             EXCLUDES(mFrameRateOverridesLock);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 0efc28b..5f64efa 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -83,8 +83,7 @@
 
 void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
     std::lock_guard lock(mLock);
-    LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first !=
-                                LayerHistory::layerStatus::NotFound,
+    LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first != LayerStatus::NotFound,
                         "%s already registered", layer->getName().c_str());
     auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
 
@@ -108,9 +107,9 @@
     auto id = layer->getSequence();
 
     auto [found, layerPair] = findLayer(id);
-    if (found == LayerHistory::layerStatus::NotFound) {
+    if (found == LayerStatus::NotFound) {
         // Offscreen layer
-        ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
+        ALOGV("%s: %s not registered", __func__, layer->getName().c_str());
         return;
     }
 
@@ -126,16 +125,15 @@
     info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
 
     // Activate layer if inactive.
-    if (found == LayerHistory::layerStatus::LayerInInactiveMap) {
+    if (found == LayerStatus::LayerInInactiveMap) {
         mActiveLayerInfos.insert(
                 {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
         mInactiveLayerInfos.erase(id);
     }
 }
 
-LayerHistory::Summary LayerHistory::summarize(const RefreshRateConfigs& refreshRateConfigs,
-                                              nsecs_t now) {
-    LayerHistory::Summary summary;
+auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> Summary {
+    Summary summary;
 
     std::lock_guard lock(mLock);
 
@@ -148,9 +146,9 @@
         ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
               layerFocused ? "" : "not");
 
-        const auto vote = info->getRefreshRateVote(refreshRateConfigs, now);
+        const auto vote = info->getRefreshRateVote(configs, now);
         // Skip NoVote layer as those don't have any requirements
-        if (vote.type == LayerHistory::LayerVoteType::NoVote) {
+        if (vote.type == LayerVoteType::NoVote) {
             continue;
         }
 
@@ -187,7 +185,7 @@
             it = mInactiveLayerInfos.erase(it);
         } else {
             if (CC_UNLIKELY(mTraceEnabled)) {
-                trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+                trace(*info, LayerVoteType::NoVote, 0);
             }
             info->onLayerInactive(now);
             it++;
@@ -224,7 +222,7 @@
             it++;
         } else {
             if (CC_UNLIKELY(mTraceEnabled)) {
-                trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+                trace(*info, LayerVoteType::NoVote, 0);
             }
             info->onLayerInactive(now);
             // move this to the inactive map
@@ -251,37 +249,23 @@
 float LayerHistory::getLayerFramerate(nsecs_t now, int32_t id) const {
     std::lock_guard lock(mLock);
     auto [found, layerPair] = findLayer(id);
-    if (found != LayerHistory::layerStatus::NotFound) {
+    if (found != LayerStatus::NotFound) {
         return layerPair->second->getFps(now).getValue();
     }
     return 0.f;
 }
 
-std::pair<LayerHistory::layerStatus, LayerHistory::LayerPair*> LayerHistory::findLayer(int32_t id) {
+auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> {
     // the layer could be in either the active or inactive map, try both
     auto it = mActiveLayerInfos.find(id);
     if (it != mActiveLayerInfos.end()) {
-        return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+        return {LayerStatus::LayerInActiveMap, &(it->second)};
     }
     it = mInactiveLayerInfos.find(id);
     if (it != mInactiveLayerInfos.end()) {
-        return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+        return {LayerStatus::LayerInInactiveMap, &(it->second)};
     }
-    return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
-}
-
-std::pair<LayerHistory::layerStatus, const LayerHistory::LayerPair*> LayerHistory::findLayer(
-        int32_t id) const {
-    // the layer could be in either the active or inactive map, try both
-    auto it = mActiveLayerInfos.find(id);
-    if (it != mActiveLayerInfos.end()) {
-        return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
-    }
-    it = mInactiveLayerInfos.find(id);
-    if (it != mInactiveLayerInfos.end()) {
-        return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
-    }
-    return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
+    return {LayerStatus::NotFound, nullptr};
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index cc55700..7b6096f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -89,7 +89,7 @@
     // worst case time complexity is O(2 * inactive + active)
     void partitionLayers(nsecs_t now) REQUIRES(mLock);
 
-    enum class layerStatus {
+    enum class LayerStatus {
         NotFound,
         LayerInActiveMap,
         LayerInInactiveMap,
@@ -97,9 +97,11 @@
 
     // looks up a layer by sequence id in both layerInfo maps.
     // The first element indicates if and where the item was found
-    std::pair<layerStatus, LayerHistory::LayerPair*> findLayer(int32_t id) REQUIRES(mLock);
-    std::pair<layerStatus, const LayerHistory::LayerPair*> findLayer(int32_t id) const
-            REQUIRES(mLock);
+    std::pair<LayerStatus, LayerPair*> findLayer(int32_t id) REQUIRES(mLock);
+
+    std::pair<LayerStatus, const LayerPair*> findLayer(int32_t id) const REQUIRES(mLock) {
+        return const_cast<LayerHistory*>(this)->findLayer(id);
+    }
 
     mutable std::mutex mLock;
 
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index a020e2c..712cd5b 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -191,7 +191,8 @@
         for (int i = 0; i < n; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
                 auto& vsync = buffer[i].vsync;
-                mHandler->dispatchFrame(vsync.vsyncId, vsync.expectedVSyncTimestamp);
+                mHandler->dispatchFrame(vsync.vsyncData.preferredVsyncId(),
+                                        vsync.vsyncData.preferredExpectedPresentationTime());
                 break;
             }
         }
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 3b9cfa6..eeeaac1 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -69,11 +69,6 @@
 using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
 using RefreshRate = RefreshRateConfigs::RefreshRate;
 
-bool RefreshRate::inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
-    using fps_approx_ops::operator<=;
-    return minRefreshRate <= getFps() && getFps() <= maxRefreshRate;
-}
-
 std::string RefreshRate::toString() const {
     return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
                               getModeId().value(), mode->getHwcId(), getFps().getValue(),
@@ -84,7 +79,7 @@
     return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d"
                               ", primary range: %s, app request range: %s",
                               defaultMode.value(), allowGroupSwitching,
-                              primaryRange.toString().c_str(), appRequestRange.toString().c_str());
+                              to_string(primaryRange).c_str(), to_string(appRequestRange).c_str());
 }
 
 std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
@@ -396,8 +391,9 @@
                 continue;
             }
 
-            bool inPrimaryRange = scores[i].refreshRate->inPolicy(policy->primaryRange.min,
-                                                                  policy->primaryRange.max);
+            const bool inPrimaryRange =
+                    policy->primaryRange.includes(scores[i].refreshRate->getFps());
+
             if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
                 !(layer.focused &&
                   (layer.vote == LayerVoteType::ExplicitDefault ||
@@ -746,7 +742,7 @@
         return false;
     }
     const RefreshRate& refreshRate = *iter->second;
-    if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) {
+    if (!policy.primaryRange.includes(refreshRate.getFps())) {
         ALOGE("Default mode is not in the primary range.");
         return false;
     }
@@ -843,7 +839,7 @@
     ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str());
 
     auto filterRefreshRates =
-            [&](Fps min, Fps max, const char* listName,
+            [&](FpsRange range, const char* rangeName,
                 std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock) {
                 getSortedRefreshRateListLocked(
                         [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
@@ -855,13 +851,13 @@
                                     mode->getDpiY() == defaultMode->getDpiY() &&
                                     (policy->allowGroupSwitching ||
                                      mode->getGroup() == defaultMode->getGroup()) &&
-                                    refreshRate.inPolicy(min, max);
+                                    range.includes(mode->getFps());
                         },
                         outRefreshRates);
 
-                LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(),
-                                    "No matching modes for %s range: min=%s max=%s", listName,
-                                    to_string(min).c_str(), to_string(max).c_str());
+                LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(), "No matching modes for %s range %s",
+                                    rangeName, to_string(range).c_str());
+
                 auto stringifyRefreshRates = [&]() -> std::string {
                     std::string str;
                     for (auto refreshRate : *outRefreshRates) {
@@ -869,13 +865,11 @@
                     }
                     return str;
                 };
-                ALOGV("%s refresh rates: %s", listName, stringifyRefreshRates().c_str());
+                ALOGV("%s refresh rates: %s", rangeName, stringifyRefreshRates().c_str());
             };
 
-    filterRefreshRates(policy->primaryRange.min, policy->primaryRange.max, "primary",
-                       &mPrimaryRefreshRates);
-    filterRefreshRates(policy->appRequestRange.min, policy->appRequestRange.max, "app request",
-                       &mAppRequestRefreshRates);
+    filterRefreshRates(policy->primaryRange, "primary", &mPrimaryRefreshRates);
+    filterRefreshRates(policy->appRequestRange, "app request", &mAppRequestRefreshRates);
 }
 
 Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ade1787..f5b97c2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -22,7 +22,6 @@
 #include <type_traits>
 #include <utility>
 
-#include <android-base/stringprintf.h>
 #include <gui/DisplayEventReceiver.h>
 
 #include <scheduler/Fps.h>
@@ -101,21 +100,6 @@
     using AllRefreshRatesMapType =
             std::unordered_map<DisplayModeId, std::unique_ptr<const RefreshRate>>;
 
-    struct FpsRange {
-        Fps min = Fps::fromValue(0.f);
-        Fps max = Fps::fromValue(std::numeric_limits<float>::max());
-
-        bool operator==(const FpsRange& other) const {
-            return isApproxEqual(min, other.min) && isApproxEqual(max, other.max);
-        }
-
-        bool operator!=(const FpsRange& other) const { return !(*this == other); }
-
-        std::string toString() const {
-            return base::StringPrintf("[%s %s]", to_string(min).c_str(), to_string(max).c_str());
-        }
-    };
-
     struct Policy {
     private:
         static constexpr int kAllowGroupSwitchingDefault = false;
@@ -140,24 +124,24 @@
 
         Policy() = default;
 
-        Policy(DisplayModeId defaultMode, const FpsRange& range)
+        Policy(DisplayModeId defaultMode, FpsRange range)
               : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {}
 
-        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, const FpsRange& range)
+        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange range)
               : Policy(defaultMode, allowGroupSwitching, range, range) {}
 
-        Policy(DisplayModeId defaultMode, const FpsRange& primaryRange,
-               const FpsRange& appRequestRange)
+        Policy(DisplayModeId defaultMode, FpsRange primaryRange, FpsRange appRequestRange)
               : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
 
-        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, const FpsRange& primaryRange,
-               const FpsRange& appRequestRange)
+        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange primaryRange,
+               FpsRange appRequestRange)
               : defaultMode(defaultMode),
                 allowGroupSwitching(allowGroupSwitching),
                 primaryRange(primaryRange),
                 appRequestRange(appRequestRange) {}
 
         bool operator==(const Policy& other) const {
+            using namespace fps_approx_ops;
             return defaultMode == other.defaultMode && primaryRange == other.primaryRange &&
                     appRequestRange == other.appRequestRange &&
                     allowGroupSwitching == other.allowGroupSwitching;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index de27bd1..82ff2fa 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -263,8 +263,12 @@
 }
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
+    const auto refreshRateConfigs = holdRefreshRateConfigs();
+    const bool supportsFrameRateOverrideByContent =
+            refreshRateConfigs->supportsFrameRateOverrideByContent();
+
     std::vector<FrameRateOverride> overrides =
-            mFrameRateOverrideMappings.getAllFrameRateOverrides();
+            mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
 
     android::EventThread* thread;
     {
@@ -531,50 +535,13 @@
 }
 
 void Scheduler::chooseRefreshRateForContent() {
-    {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        if (!mRefreshRateConfigs->canSwitch()) return;
-    }
+    const auto configs = holdRefreshRateConfigs();
+    if (!configs->canSwitch()) return;
 
     ATRACE_CALL();
 
-    DisplayModePtr newMode;
-    GlobalSignals consideredSignals;
-
-    bool frameRateChanged;
-    bool frameRateOverridesChanged;
-
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
-    LayerHistory::Summary summary = mLayerHistory.summarize(*refreshRateConfigs, systemTime());
-    {
-        std::lock_guard<std::mutex> lock(mPolicyLock);
-        mPolicy.contentRequirements = std::move(summary);
-
-        std::tie(newMode, consideredSignals) = chooseDisplayMode();
-        frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
-
-        if (mPolicy.mode == newMode) {
-            // We don't need to change the display mode, but we might need to send an event
-            // about a mode change, since it was suppressed due to a previous idleConsidered
-            if (!consideredSignals.idle) {
-                dispatchCachedReportedMode();
-            }
-            frameRateChanged = false;
-        } else {
-            mPolicy.mode = newMode;
-            frameRateChanged = true;
-        }
-    }
-    if (frameRateChanged) {
-        const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
-
-        mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                             consideredSignals.idle ? DisplayModeEvent::None
-                                                                    : DisplayModeEvent::Changed);
-    }
-    if (frameRateOverridesChanged) {
-        mSchedulerCallback.triggerOnFrameRateOverridesChanged();
-    }
+    LayerHistory::Summary summary = mLayerHistory.summarize(*configs, systemTime());
+    applyPolicy(&Policy::contentRequirements, std::move(summary));
 }
 
 void Scheduler::resetIdleTimer() {
@@ -636,7 +603,7 @@
 }
 
 void Scheduler::idleTimerCallback(TimerState state) {
-    handleTimerStateChanged(&mPolicy.idleTimer, state);
+    applyPolicy(&Policy::idleTimer, state);
     ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
 }
 
@@ -646,14 +613,14 @@
     // Clear layer history to get fresh FPS detection.
     // NOTE: Instead of checking all the layers, we should be checking the layer
     // that is currently on top. b/142507166 will give us this capability.
-    if (handleTimerStateChanged(&mPolicy.touch, touch)) {
+    if (applyPolicy(&Policy::touch, touch).touch) {
         mLayerHistory.clear();
     }
     ATRACE_INT("TouchState", static_cast<int>(touch));
 }
 
 void Scheduler::displayPowerTimerCallback(TimerState state) {
-    handleTimerStateChanged(&mPolicy.displayPowerTimer, state);
+    applyPolicy(&Policy::displayPowerTimer, state);
     ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
 }
 
@@ -682,10 +649,10 @@
 
 bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
     const auto refreshRateConfigs = holdRefreshRateConfigs();
-    if (!refreshRateConfigs->supportsFrameRateOverrideByContent()) {
-        return false;
-    }
 
+    // we always update mFrameRateOverridesByContent here
+    // supportsFrameRateOverridesByContent will be checked
+    // when getting FrameRateOverrides from mFrameRateOverrideMappings
     if (!consideredSignals.idle) {
         const auto frameRateOverrides =
                 refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
@@ -695,8 +662,8 @@
     return false;
 }
 
-template <class T>
-bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
+template <typename S, typename T>
+auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
     DisplayModePtr newMode;
     GlobalSignals consideredSignals;
 
@@ -706,15 +673,17 @@
     const auto refreshRateConfigs = holdRefreshRateConfigs();
     {
         std::lock_guard<std::mutex> lock(mPolicyLock);
-        if (*currentState == newState) {
-            return false;
-        }
-        *currentState = newState;
+
+        auto& currentState = mPolicy.*statePtr;
+        if (currentState == newState) return {};
+        currentState = std::forward<T>(newState);
+
         std::tie(newMode, consideredSignals) = chooseDisplayMode();
         frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
+
         if (mPolicy.mode == newMode) {
             // We don't need to change the display mode, but we might need to send an event
-            // about a mode change, since it was suppressed due to a previous idleConsidered
+            // about a mode change, since it was suppressed if previously considered idle.
             if (!consideredSignals.idle) {
                 dispatchCachedReportedMode();
             }
@@ -733,7 +702,7 @@
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
     }
-    return consideredSignals.touch;
+    return consideredSignals;
 }
 
 auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 468c4cc..9c32b1f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -263,14 +263,17 @@
     void touchTimerCallback(TimerState);
     void displayPowerTimerCallback(TimerState);
 
-    // handles various timer features to change the refresh rate.
-    template <class T>
-    bool handleTimerStateChanged(T* currentState, T newState);
-
     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
+    // that fulfills the new policy if the state changed. Returns the signals that were considered.
+    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);
 
@@ -323,7 +326,7 @@
 
     mutable std::mutex mPolicyLock;
 
-    struct {
+    struct Policy {
         // Policy for choosing the display mode.
         LayerHistory::Summary contentRequirements;
         TimerState idleTimer = TimerState::Reset;
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index 245db0f..be57b2a 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -134,16 +134,27 @@
     return mVsyncConfig;
 }
 
-const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+auto VsyncModulator::getNextVsyncConfigType() const -> VsyncConfigType {
     // Early offsets are used if we're in the middle of a refresh rate
     // change, or if we recently begin a transaction.
     if (!mEarlyWakeupRequests.empty() || mTransactionSchedule == Schedule::EarlyEnd ||
         mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
-        return mVsyncConfigSet.early;
+        return VsyncConfigType::Early;
     } else if (mEarlyGpuFrames > 0) {
-        return mVsyncConfigSet.earlyGpu;
+        return VsyncConfigType::EarlyGpu;
     } else {
-        return mVsyncConfigSet.late;
+        return VsyncConfigType::Late;
+    }
+}
+
+const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+    switch (getNextVsyncConfigType()) {
+        case VsyncConfigType::Early:
+            return mVsyncConfigSet.early;
+        case VsyncConfigType::EarlyGpu:
+            return mVsyncConfigSet.earlyGpu;
+        case VsyncConfigType::Late:
+            return mVsyncConfigSet.late;
     }
 }
 
@@ -176,4 +187,9 @@
     static_cast<void>(updateVsyncConfigLocked());
 }
 
+bool VsyncModulator::isVsyncConfigDefault() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return getNextVsyncConfigType() == VsyncConfigType::Late;
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index 2000c54..537cae1 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -109,11 +109,16 @@
 
     [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
 
+    [[nodiscard]] bool isVsyncConfigDefault() const;
+
 protected:
     // Called from unit tests as well
     void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
 
 private:
+    enum class VsyncConfigType { Early, EarlyGpu, Late };
+
+    VsyncConfigType getNextVsyncConfigType() const REQUIRES(mMutex);
     const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex);
     [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex);
     [[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex);
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 639b3e5..bd4f409 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cmath>
+#include <limits>
 #include <ostream>
 #include <string>
 #include <type_traits>
@@ -60,6 +61,13 @@
     nsecs_t mPeriod = 0;
 };
 
+struct FpsRange {
+    Fps min = Fps::fromValue(0.f);
+    Fps max = Fps::fromValue(std::numeric_limits<float>::max());
+
+    bool includes(Fps) const;
+};
+
 static_assert(std::is_trivially_copyable_v<Fps>);
 
 constexpr Fps operator""_Hz(unsigned long long frequency) {
@@ -111,8 +119,21 @@
     return !isApproxLess(lhs, rhs);
 }
 
+inline bool operator==(FpsRange lhs, FpsRange rhs) {
+    return isApproxEqual(lhs.min, rhs.min) && isApproxEqual(lhs.max, rhs.max);
+}
+
+inline bool operator!=(FpsRange lhs, FpsRange rhs) {
+    return !(lhs == rhs);
+}
+
 } // namespace fps_approx_ops
 
+inline bool FpsRange::includes(Fps fps) const {
+    using fps_approx_ops::operator<=;
+    return min <= fps && fps <= max;
+}
+
 struct FpsApproxEqual {
     bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
 };
@@ -125,4 +146,9 @@
     return stream << to_string(fps);
 }
 
+inline std::string to_string(FpsRange range) {
+    const auto [min, max] = range;
+    return base::StringPrintf("[%s, %s]", to_string(min).c_str(), to_string(max).c_str());
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index df8f6e6..dc88793 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -50,6 +50,7 @@
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
 #include <ftl/future.h>
+#include <ftl/small_map.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
 #include <gui/IProducerListener.h>
@@ -141,19 +142,21 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
 #define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
 
+// Note: The parentheses around `expr` are needed to deduce an lvalue or rvalue reference.
 #define ON_MAIN_THREAD(expr)                                       \
-    [&] {                                                          \
+    [&]() -> decltype(auto) {                                      \
         LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \
         UnnecessaryLock lock(mStateLock);                          \
         return (expr);                                             \
     }()
 
 #define MAIN_THREAD_GUARD(expr)                                    \
-    [&] {                                                          \
+    [&]() -> decltype(auto) {                                      \
         LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \
         MainThreadScopedGuard lock(SF_MAIN_THREAD);                \
         return (expr);                                             \
@@ -163,6 +166,7 @@
 #define NO_THREAD_SAFETY_ANALYSIS \
     _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"")
 
+using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 
@@ -248,11 +252,6 @@
     const status_t status;
 };
 
-struct SCOPED_CAPABILITY UnnecessaryLock {
-    explicit UnnecessaryLock(Mutex& mutex) ACQUIRE(mutex) {}
-    ~UnnecessaryLock() RELEASE() {}
-};
-
 // TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
 constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
 
@@ -498,8 +497,6 @@
 
     mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
 
-    enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
-
     if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
         mTransactionTracing.emplace();
     }
@@ -508,11 +505,13 @@
 LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
     if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
         return LatchUnsignaledConfig::Always;
-    } else if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) {
-        return LatchUnsignaledConfig::Auto;
-    } else {
-        return LatchUnsignaledConfig::Disabled;
     }
+
+    if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) {
+        return LatchUnsignaledConfig::AutoSingleLayer;
+    }
+
+    return LatchUnsignaledConfig::Disabled;
 }
 
 SurfaceFlinger::~SurfaceFlinger() = default;
@@ -854,6 +853,8 @@
     mCompositionEngine->getHwComposer().setCallback(*this);
     ClientCache::getInstance().setRenderEngine(&getRenderEngine());
 
+    enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
+
     if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) {
         enableHalVirtualDisplays(true);
     }
@@ -1345,9 +1346,8 @@
     auto future = mScheduler->schedule([=]() MAIN_THREAD {
         ATRACE_CALL();
         if (mPowerAdvisor.isUsingExpensiveRendering()) {
-            const auto& displays = ON_MAIN_THREAD(mDisplays);
-            for (const auto& [_, display] : displays) {
-                const static constexpr auto kDisable = false;
+            for (const auto& [_, display] : mDisplays) {
+                constexpr bool kDisable = false;
                 mPowerAdvisor.setExpensiveRenderingExpected(display->getId(), kDisable);
             }
         }
@@ -1818,8 +1818,9 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDisplayDecorationSupport(const sp<IBinder>& displayToken,
-                                                     bool* outSupport) const {
+status_t SurfaceFlinger::getDisplayDecorationSupport(
+        const sp<IBinder>& displayToken,
+        std::optional<DisplayDecorationSupport>* outSupport) const {
     if (!displayToken || !outSupport) {
         return BAD_VALUE;
     }
@@ -1830,8 +1831,7 @@
     if (!displayId) {
         return NAME_NOT_FOUND;
     }
-    *outSupport =
-            getHwComposer().hasDisplayCapability(*displayId, DisplayCapability::DISPLAY_DECORATION);
+    getHwComposer().getDisplayDecorationSupport(*displayId, outSupport);
     return NO_ERROR;
 }
 
@@ -2362,9 +2362,11 @@
     // something (such as user input) to an accurate diasplay time.
     // Snapping also allows an app to precisely calculate
     // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
-    nsecs_t bias = stats.vsyncPeriod / 2;
-    int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
-    nsecs_t snappedCompositeToPresentLatency =
+    const nsecs_t bias = stats.vsyncPeriod / 2;
+    const int64_t extraVsyncs = (stats.vsyncPeriod) > 0 ?
+        ((compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod) :
+        0;
+    const nsecs_t snappedCompositeToPresentLatency =
             (extraVsyncs > 0) ? idealLatency + (extraVsyncs * stats.vsyncPeriod) : idealLatency;
 
     std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
@@ -3234,9 +3236,7 @@
         return;
     }
 
-    const auto& displays = ON_MAIN_THREAD(mDisplays);
-
-    for (const auto& [_, display] : displays) {
+    for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
         if (const auto brightness = display->getStagedBrightness(); brightness) {
             if (!needsComposite) {
                 const status_t error =
@@ -3257,60 +3257,48 @@
 
 void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
                                       std::vector<DisplayInfo>& outDisplayInfos) {
-    struct Details {
-        Details(bool receivesInput, bool isSecure, const ui::Transform& transform,
-                const DisplayInfo& info)
-              : receivesInput(receivesInput),
-                isSecure(isSecure),
-                transform(std::move(transform)),
-                info(std::move(info)) {}
-        bool receivesInput;
-        bool isSecure;
-        ui::Transform transform;
-        DisplayInfo info;
-    };
-    std::unordered_map<uint32_t /*layerStackId*/, Details> inputDisplayDetails;
+    ftl::SmallMap<ui::LayerStack, DisplayDevice::InputInfo, 4> displayInputInfos;
+
     for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
-        const uint32_t layerStackId = display->getLayerStack().id;
-        const auto& [info, transform] = display->getInputInfo();
-        const auto& [it, emplaced] =
-                inputDisplayDetails.try_emplace(layerStackId, display->receivesInput(),
-                                                display->isSecure(), transform, info);
+        const auto layerStack = display->getLayerStack();
+        const auto info = display->getInputInfo();
+
+        const auto [it, emplaced] = displayInputInfos.try_emplace(layerStack, info);
         if (emplaced) {
             continue;
         }
 
-        // There is more than one display for the layerStack. In this case, the display that is
-        // configured to receive input takes precedence.
-        auto& details = it->second;
-        if (!display->receivesInput()) {
-            continue;
+        // If the layer stack is mirrored on multiple displays, the first display that is configured
+        // to receive input takes precedence.
+        auto& otherInfo = it->second;
+        if (otherInfo.receivesInput) {
+            ALOGW_IF(display->receivesInput(),
+                     "Multiple displays claim to accept input for the same layer stack: %u",
+                     layerStack.id);
+        } else {
+            otherInfo = info;
         }
-        ALOGE_IF(details.receivesInput,
-                 "Multiple displays claim to accept input for the same layer stack: %u",
-                 layerStackId);
-        details.receivesInput = display->receivesInput();
-        details.isSecure = display->isSecure();
-        details.transform = std::move(transform);
-        details.info = std::move(info);
     }
 
+    static size_t sNumWindowInfos = 0;
+    outWindowInfos.reserve(sNumWindowInfos);
+    sNumWindowInfos = 0;
+
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (!layer->needsInputInfo()) return;
 
-        const uint32_t layerStackId = layer->getLayerStack().id;
-        const auto it = inputDisplayDetails.find(layerStackId);
-        if (it == inputDisplayDetails.end()) {
-            // Do not create WindowInfos for windows on displays that cannot receive input.
-            return;
+        // Do not create WindowInfos for windows on displays that cannot receive input.
+        if (const auto opt = displayInputInfos.get(layer->getLayerStack())) {
+            const auto& info = opt->get();
+            outWindowInfos.push_back(layer->fillInputInfo(info.transform, info.isSecure));
         }
-
-        const auto& details = it->second;
-        outWindowInfos.push_back(layer->fillInputInfo(details.transform, details.isSecure));
     });
 
-    for (const auto& [_, details] : inputDisplayDetails) {
-        outDisplayInfos.push_back(std::move(details.info));
+    sNumWindowInfos = outWindowInfos.size();
+
+    outDisplayInfos.reserve(displayInputInfos.size());
+    for (const auto& [_, info] : displayInputInfos) {
+        outDisplayInfos.push_back(info.info);
     }
 }
 
@@ -3645,6 +3633,19 @@
     return old;
 }
 
+bool SurfaceFlinger::stopTransactionProcessing(
+        const std::unordered_set<sp<IBinder>, SpHash<IBinder>>&
+                applyTokensWithUnsignaledTransactions) const {
+    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
+        // if we are in LatchUnsignaledConfig::AutoSingleLayer
+        // then we should have only one applyToken for processing.
+        // so we can stop further transactions on this applyToken.
+        return !applyTokensWithUnsignaledTransactions.empty();
+    }
+
+    return false;
+}
+
 bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
@@ -3652,44 +3653,44 @@
     std::vector<TransactionState> transactions;
     // Layer handles that have transactions with buffers that are ready to be applied.
     std::unordered_set<sp<IBinder>, SpHash<IBinder>> bufferLayersReadyToPresent;
+    std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
     {
         Mutex::Autolock _l(mStateLock);
         {
             Mutex::Autolock _l(mQueueLock);
-            // allowLatchUnsignaled acts as a filter condition when latch unsignaled is either auto
-            // or always. auto: in this case we let buffer latch unsignaled if we have only one
-            // applyToken and if only first transaction is latch unsignaled. If more than one
-            // applyToken we don't latch unsignaled.
-            bool allowLatchUnsignaled = allowedLatchUnsignaled();
-            bool isFirstUnsignaledTransactionApplied = false;
             // Collect transactions from pending transaction queue.
             auto it = mPendingTransactionQueues.begin();
             while (it != mPendingTransactionQueues.end()) {
                 auto& [applyToken, transactionQueue] = *it;
                 while (!transactionQueue.empty()) {
+                    if (stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
+                        ATRACE_NAME("stopTransactionProcessing");
+                        break;
+                    }
+
                     auto& transaction = transactionQueue.front();
-                    if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
-                                                       transaction.isAutoTimestamp,
-                                                       transaction.desiredPresentTime,
-                                                       transaction.originUid, transaction.states,
-                                                       bufferLayersReadyToPresent,
-                                                       allowLatchUnsignaled)) {
+                    const auto ready =
+                            transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+                                                          transaction.isAutoTimestamp,
+                                                          transaction.desiredPresentTime,
+                                                          transaction.originUid, transaction.states,
+                                                          bufferLayersReadyToPresent,
+                                                          transactions.size());
+                    ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
+                    if (ready == TransactionReadiness::NotReady) {
                         setTransactionFlags(eTransactionFlushNeeded);
                         break;
                     }
                     transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
                         bufferLayersReadyToPresent.insert(state.surface);
                     });
+                    const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
+                    if (appliedUnsignaled) {
+                        applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
+                    }
+
                     transactions.emplace_back(std::move(transaction));
                     transactionQueue.pop();
-                    if (allowLatchUnsignaled &&
-                        enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
-                        // if allowLatchUnsignaled && we are in LatchUnsignaledConfig::Auto
-                        // then we should have only one applyToken for processing.
-                        // so we can stop further transactions on this applyToken.
-                        isFirstUnsignaledTransactionApplied = true;
-                        break;
-                    }
                 }
 
                 if (transactionQueue.empty()) {
@@ -3707,25 +3708,35 @@
             // Case 3: others are the transactions that are ready to apply.
             while (!mTransactionQueue.empty()) {
                 auto& transaction = mTransactionQueue.front();
-                bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
+                const bool pendingTransactions =
+                        mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
-                if (isFirstUnsignaledTransactionApplied || pendingTransactions ||
-                    !transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
-                                                   transaction.isAutoTimestamp,
-                                                   transaction.desiredPresentTime,
-                                                   transaction.originUid, transaction.states,
-                                                   bufferLayersReadyToPresent,
-                                                   allowLatchUnsignaled)) {
+                const auto ready = [&]() REQUIRES(mStateLock) {
+                    if (pendingTransactions ||
+                        stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
+                        ATRACE_NAME("pendingTransactions || stopTransactionProcessing");
+                        return TransactionReadiness::NotReady;
+                    }
+
+                    return transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+                                                         transaction.isAutoTimestamp,
+                                                         transaction.desiredPresentTime,
+                                                         transaction.originUid, transaction.states,
+                                                         bufferLayersReadyToPresent,
+                                                         transactions.size());
+                }();
+                ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
+                if (ready == TransactionReadiness::NotReady) {
                     mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
                     transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
                         bufferLayersReadyToPresent.insert(state.surface);
                     });
-                    transactions.emplace_back(std::move(transaction));
-                    if (allowLatchUnsignaled &&
-                        enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
-                        isFirstUnsignaledTransactionApplied = true;
+                    const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
+                    if (appliedUnsignaled) {
+                        applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
                     }
+                    transactions.emplace_back(std::move(transaction));
                 }
                 mTransactionQueue.pop_front();
                 ATRACE_INT("TransactionQueue", mTransactionQueue.size());
@@ -3762,62 +3773,6 @@
     return needsTraversal;
 }
 
-bool SurfaceFlinger::allowedLatchUnsignaled() {
-    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
-        return false;
-    }
-    // Always mode matches the current latch unsignaled behavior.
-    // This behavior is currently used by the partners and we would like
-    // to keep it until we are completely migrated to Auto mode successfully
-    // and we we have our fallback based implementation in place.
-    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
-        return true;
-    }
-
-    //  if enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto
-    //  we don't latch unsignaled if more than one applyToken, as it can backpressure
-    //  the other transactions.
-    if (mPendingTransactionQueues.size() > 1) {
-        return false;
-    }
-    std::optional<sp<IBinder>> applyToken = std::nullopt;
-    bool isPendingTransactionQueuesItem = false;
-    if (!mPendingTransactionQueues.empty()) {
-        applyToken = mPendingTransactionQueues.begin()->first;
-        isPendingTransactionQueuesItem = true;
-    }
-
-    for (const auto& item : mTransactionQueue) {
-        if (!applyToken.has_value()) {
-            applyToken = item.applyToken;
-        } else if (applyToken.has_value() && applyToken != item.applyToken) {
-            return false;
-        }
-    }
-
-    if (isPendingTransactionQueuesItem) {
-        return checkTransactionCanLatchUnsignaled(
-                mPendingTransactionQueues.begin()->second.front());
-    } else if (applyToken.has_value()) {
-        return checkTransactionCanLatchUnsignaled((mTransactionQueue.front()));
-    }
-    return false;
-}
-
-bool SurfaceFlinger::checkTransactionCanLatchUnsignaled(const TransactionState& transaction) {
-    if (transaction.states.size() == 1) {
-        const auto& state = transaction.states.begin()->state;
-        if ((state.flags & ~layer_state_t::eBufferChanged) == 0 &&
-            state.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
-            state.bufferData->acquireFence &&
-            state.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled) {
-            ATRACE_NAME("transactionCanLatchUnsignaled");
-            return true;
-        }
-    }
-    return false;
-}
-
 bool SurfaceFlinger::transactionFlushNeeded() {
     Mutex::Autolock _l(mQueueLock);
     return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
@@ -3844,12 +3799,57 @@
     return prediction->presentTime >= expectedPresentTime &&
             prediction->presentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
 }
+bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
+                                           size_t numStates, size_t totalTXapplied) const {
+    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
+        ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
+        return false;
+    }
 
-bool SurfaceFlinger::transactionIsReadyToBeApplied(
+    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
+        ALOGV("%s: true (LatchUnsignaledConfig::Always)", __func__);
+        return true;
+    }
+
+    // We only want to latch unsignaled when a single layer is updated in this
+    // transaction (i.e. not a blast sync transaction).
+    if (numStates != 1) {
+        ALOGV("%s: false (numStates=%zu)", __func__, numStates);
+        return false;
+    }
+
+    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
+        if (totalTXapplied > 0) {
+            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)",
+                  __func__, totalTXapplied);
+            return false;
+        }
+
+        // We don't want to latch unsignaled if are in early / client composition
+        // as it leads to jank due to RenderEngine waiting for unsignaled buffer
+        // or window animations being slow.
+        const auto isDefaultVsyncConfig = mVsyncModulator->isVsyncConfigDefault();
+        if (!isDefaultVsyncConfig) {
+            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; !isDefaultVsyncConfig)",
+                  __func__);
+            return false;
+        }
+    }
+
+    if (!layer->simpleBufferUpdate(state)) {
+        ALOGV("%s: false (!simpleBufferUpdate)", __func__);
+        return false;
+    }
+
+    ALOGV("%s: true", __func__);
+    return true;
+}
+
+auto SurfaceFlinger::transactionIsReadyToBeApplied(
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
         const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
-        bool allowLatchUnsignaled) const {
+        size_t totalTXapplied) const -> TransactionReadiness {
     ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
@@ -3857,31 +3857,24 @@
     if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
         desiredPresentTime < expectedPresentTime + s2ns(1)) {
         ATRACE_NAME("not current");
-        return false;
+        return TransactionReadiness::NotReady;
     }
 
     if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
         ATRACE_NAME("!isVsyncValid");
-        return false;
+        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(expectedPresentTime, info.vsyncId)) {
         ATRACE_NAME("frameIsEarly");
-        return false;
+        return TransactionReadiness::NotReady;
     }
 
+    bool fenceUnsignaled = false;
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
-        const bool acquireFenceChanged = s.bufferData &&
-                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged);
-        if (acquireFenceChanged && s.bufferData->acquireFence && !allowLatchUnsignaled &&
-            s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled) {
-            ATRACE_NAME("fence unsignaled");
-            return false;
-        }
-
         sp<Layer> layer = nullptr;
         if (s.surface) {
             layer = fromHandle(s.surface).promote();
@@ -3893,7 +3886,22 @@
             continue;
         }
 
-        ATRACE_NAME(layer->getName().c_str());
+        const bool allowLatchUnsignaled =
+                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) {
+            ATRACE_NAME("fence unsignaled");
+            return TransactionReadiness::NotReady;
+        }
 
         if (s.hasBufferChanges()) {
             // If backpressure is enabled and we already have a buffer to commit, keep the
@@ -3902,11 +3910,11 @@
                     bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
             if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
                 ATRACE_NAME("hasPendingBuffer");
-                return false;
+                return TransactionReadiness::NotReady;
             }
         }
     }
-    return true;
+    return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready;
 }
 
 void SurfaceFlinger::queueTransaction(TransactionState& state) {
@@ -5093,11 +5101,9 @@
 }
 
 LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
-    const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-
     LayersProto layersProto;
     for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
-        layer->writeToProto(layersProto, traceFlags, display.get());
+        layer->writeToProto(layersProto, traceFlags);
     }
 
     return layersProto;
@@ -5138,8 +5144,7 @@
         rootProto->add_children(offscreenLayer->sequence);
 
         // Add layer
-        LayerProto* layerProto =
-                offscreenLayer->writeToProto(layersProto, traceFlags, nullptr /*device*/);
+        LayerProto* layerProto = offscreenLayer->writeToProto(layersProto, traceFlags);
         layerProto->set_parent(offscreenRootLayerId);
     }
 }
@@ -5336,7 +5341,7 @@
     colorizer.reset(result);
     const bool hwcDisabled = mDebugDisableHWC || mDebugFlashDelay;
     StringAppendF(&result, "  h/w composer %s\n", hwcDisabled ? "disabled" : "enabled");
-    getHwComposer().dump(result);
+    dumpHwc(result);
 
     /*
      * Dump gralloc state
@@ -5734,9 +5739,9 @@
             }
             case 1025: { // Set layer tracing
                 n = data.readInt32();
-                int64_t fixedStartingTime = data.readInt64();
                 bool tracingEnabledChanged;
-                if (n) {
+                if (n == 1) {
+                    int64_t fixedStartingTime = data.readInt64();
                     ALOGD("LayerTracing enabled");
                     tracingEnabledChanged = mLayerTracing.enable();
                     if (tracingEnabledChanged) {
@@ -5748,6 +5753,10 @@
                                 })
                                 .wait();
                     }
+                } else if (n == 2) {
+                    std::string filename = std::string(data.readCString());
+                    ALOGD("LayerTracing disabled. Trace wrote to %s", filename.c_str());
+                    tracingEnabledChanged = mLayerTracing.disable(filename.c_str());
                 } else {
                     ALOGD("LayerTracing disabled");
                     tracingEnabledChanged = mLayerTracing.disable();
@@ -6000,9 +6009,9 @@
                         mTransactionTracing->setBufferSize(
                                 TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
                     } else {
+                        mTransactionTracing->writeToFile();
                         mTransactionTracing->setBufferSize(
                                 TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
-                        mTransactionTracing->writeToFile();
                     }
                 }
                 reply->writeInt32(NO_ERROR);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a7cd579..3ecce33 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -85,6 +85,8 @@
 #include <unordered_set>
 #include <utility>
 
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+
 using namespace android::surfaceflinger;
 
 namespace android {
@@ -137,7 +139,20 @@
     eTransactionMask = 0x1f,
 };
 
-enum class LatchUnsignaledConfig { Always, Auto, Disabled };
+// Latch Unsignaled buffer behaviours
+enum class LatchUnsignaledConfig {
+    // All buffers are latched signaled.
+    Disabled,
+
+    // Latch unsignaled is permitted when a single layer is updated in a frame,
+    // and the update includes just a buffer update (i.e. no sync transactions
+    // or geometry changes).
+    AutoSingleLayer,
+
+    // All buffers are latched unsignaled. This behaviour is discouraged as it
+    // can break sync transactions, stall the display and cause undesired side effects.
+    Always,
+};
 
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
@@ -162,6 +177,11 @@
     std::atomic<nsecs_t> mLastSwapTime = 0;
 };
 
+struct SCOPED_CAPABILITY UnnecessaryLock {
+    explicit UnnecessaryLock(Mutex& mutex) ACQUIRE(mutex) {}
+    ~UnnecessaryLock() RELEASE() {}
+};
+
 class SurfaceFlinger : public BnSurfaceComposer,
                        public PriorityDumper,
                        private IBinder::DeathRecipient,
@@ -596,8 +616,10 @@
     status_t notifyPowerBoost(int32_t boostId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
-    status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
-                                         bool* outSupport) const override;
+    status_t getDisplayDecorationSupport(
+            const sp<IBinder>& displayToken,
+            std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                    outSupport) const override;
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                           int8_t compatibility, int8_t changeFrameRateStrategy) override;
 
@@ -749,16 +771,21 @@
     uint32_t setTransactionFlags(uint32_t mask, TransactionSchedule,
                                  const sp<IBinder>& applyToken = {});
     void commitOffscreenLayers();
-    bool transactionIsReadyToBeApplied(
+    enum class TransactionReadiness {
+        NotReady,
+        Ready,
+        ReadyUnsignaled,
+    };
+    TransactionReadiness transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
             uid_t originUid, const Vector<ComposerState>& states,
             const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& bufferLayersReadyToPresent,
-            bool allowLatchUnsignaled) const REQUIRES(mStateLock);
+            size_t totalTXapplied) const REQUIRES(mStateLock);
     static LatchUnsignaledConfig getLatchUnsignaledConfig();
-    bool latchUnsignaledIsAllowed(std::vector<TransactionState>& transactions) REQUIRES(mStateLock);
-    bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock);
-    bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction)
-            REQUIRES(mStateLock);
+    bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
+                               size_t totalTXapplied) const;
+    bool stopTransactionProcessing(const std::unordered_set<sp<IBinder>, SpHash<IBinder>>&
+                                           applyTokensWithUnsignaledTransactions) const;
     bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 006efdf..49554c7 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -45,14 +45,14 @@
     return true;
 }
 
-bool LayerTracing::disable() {
+bool LayerTracing::disable(std::string filename) {
     std::scoped_lock lock(mTraceLock);
     if (!mEnabled) {
         return false;
     }
     mEnabled = false;
     LayersTraceFileProto fileProto = createTraceFileProto();
-    mBuffer->writeToFile(fileProto, FILE_NAME);
+    mBuffer->writeToFile(fileProto, filename);
     mBuffer->reset();
     return true;
 }
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index bd448c9..88a19ec 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -43,7 +43,7 @@
     LayerTracing(SurfaceFlinger& flinger);
     ~LayerTracing();
     bool enable();
-    bool disable();
+    bool disable(std::string filename = FILE_NAME);
     bool isEnabled() const;
     status_t writeToFile();
     LayersTraceFileProto createTraceFileProto() const;
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
index 3b2626d..7e38c55 100644
--- a/services/surfaceflinger/Tracing/RingBuffer.h
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -67,7 +67,7 @@
         // -rw-r--r--
         const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
         if (!android::base::WriteStringToFile(output, filename, mode, getuid(), getgid(), true)) {
-            ALOGE("Could not save the proto file.");
+            ALOGE("Could not save the proto file %s", filename.c_str());
             return PERMISSION_DENIED;
         }
         return NO_ERROR;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index a91698f..1e5c3e7 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -22,9 +22,7 @@
 
 namespace android::surfaceflinger {
 
-proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t,
-                                                        LayerHandleToIdFn getLayerId,
-                                                        DisplayHandleToIdFn getDisplayId) {
+proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t) {
     proto::TransactionState proto;
     proto.set_pid(t.originPid);
     proto.set_uid(t.originUid);
@@ -33,12 +31,14 @@
     proto.set_post_time(t.postTime);
     proto.set_transaction_id(t.id);
 
+    proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(t.states.size()));
     for (auto& layerState : t.states) {
-        proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state, getLayerId)));
+        proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state)));
     }
 
+    proto.mutable_display_changes()->Reserve(static_cast<int32_t>(t.displays.size()));
     for (auto& displayState : t.displays) {
-        proto.mutable_display_changes()->Add(std::move(toProto(displayState, getDisplayId)));
+        proto.mutable_display_changes()->Add(std::move(toProto(displayState)));
     }
     return proto;
 }
@@ -46,8 +46,9 @@
 proto::TransactionState TransactionProtoParser::toProto(
         const std::map<int32_t /* layerId */, TracingLayerState>& states) {
     proto::TransactionState proto;
+    proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(states.size()));
     for (auto& [layerId, state] : states) {
-        proto::LayerState layerProto = toProto(state, nullptr);
+        proto::LayerState layerProto = toProto(state);
         if (layerProto.has_buffer_data()) {
             proto::LayerState_BufferData* bufferProto = layerProto.mutable_buffer_data();
             bufferProto->set_buffer_id(state.bufferId);
@@ -69,11 +70,10 @@
     return proto;
 }
 
-proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer,
-                                                  LayerHandleToIdFn getLayerId) {
+proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) {
     proto::LayerState proto;
-    if (getLayerId != nullptr) {
-        proto.set_layer_id(getLayerId(layer.surface));
+    if (layer.surface) {
+        proto.set_layer_id(mMapper->getLayerId(layer.surface));
     } else {
         proto.set_layer_id(layer.layerId);
     }
@@ -136,13 +136,27 @@
     }
     if (layer.what & layer_state_t::eBufferChanged) {
         proto::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
-        if (layer.bufferData->buffer) {
+        if (layer.bufferData->hasBuffer()) {
             bufferProto->set_buffer_id(layer.bufferData->getId());
             bufferProto->set_width(layer.bufferData->getWidth());
             bufferProto->set_height(layer.bufferData->getHeight());
             bufferProto->set_pixel_format(static_cast<proto::LayerState_BufferData_PixelFormat>(
                     layer.bufferData->getPixelFormat()));
             bufferProto->set_usage(layer.bufferData->getUsage());
+        } else {
+            uint64_t bufferId;
+            uint32_t width;
+            uint32_t height;
+            int32_t pixelFormat;
+            uint64_t usage;
+            mMapper->getGraphicBufferPropertiesFromCache(layer.bufferData->cachedBuffer, &bufferId,
+                                                         &width, &height, &pixelFormat, &usage);
+            bufferProto->set_buffer_id(bufferId);
+            bufferProto->set_width(width);
+            bufferProto->set_height(height);
+            bufferProto->set_pixel_format(
+                    static_cast<proto::LayerState_BufferData_PixelFormat>(pixelFormat));
+            bufferProto->set_usage(usage);
         }
         bufferProto->set_frame_number(layer.bufferData->frameNumber);
         bufferProto->set_flags(layer.bufferData->flags.get());
@@ -165,15 +179,15 @@
         }
     }
 
-    if ((layer.what & layer_state_t::eReparent) && getLayerId != nullptr) {
+    if (layer.what & layer_state_t::eReparent) {
         int32_t layerId = layer.parentSurfaceControlForChild
-                ? getLayerId(layer.parentSurfaceControlForChild->getHandle())
+                ? mMapper->getLayerId(layer.parentSurfaceControlForChild->getHandle())
                 : -1;
         proto.set_parent_id(layerId);
     }
-    if ((layer.what & layer_state_t::eRelativeLayerChanged) && getLayerId != nullptr) {
+    if (layer.what & layer_state_t::eRelativeLayerChanged) {
         int32_t layerId = layer.relativeLayerSurfaceControl
-                ? getLayerId(layer.relativeLayerSurfaceControl->getHandle())
+                ? mMapper->getLayerId(layer.relativeLayerSurfaceControl->getHandle())
                 : -1;
         proto.set_relative_parent_id(layerId);
         proto.set_z(layer.z);
@@ -183,13 +197,16 @@
         if (layer.windowInfoHandle) {
             const gui::WindowInfo* inputInfo = layer.windowInfoHandle->getInfo();
             proto::LayerState_WindowInfo* windowInfoProto = proto.mutable_window_info_handle();
-            windowInfoProto->set_layout_params_flags(inputInfo->flags.get());
-            windowInfoProto->set_layout_params_type(static_cast<int32_t>(inputInfo->type));
+            windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
+            windowInfoProto->set_layout_params_type(
+                    static_cast<int32_t>(inputInfo->layoutParamsType));
             LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
                                            windowInfoProto->mutable_touchable_region());
             windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
-            windowInfoProto->set_focusable(inputInfo->focusable);
-            windowInfoProto->set_has_wallpaper(inputInfo->hasWallpaper);
+            windowInfoProto->set_focusable(
+                    !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
+            windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
+                    gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
             windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
             proto::LayerState_Transform* transformProto = windowInfoProto->mutable_transform();
             transformProto->set_dsdx(inputInfo->transform.dsdx());
@@ -200,12 +217,8 @@
             transformProto->set_ty(inputInfo->transform.ty());
             windowInfoProto->set_replace_touchable_region_with_crop(
                     inputInfo->replaceTouchableRegionWithCrop);
-            if (getLayerId != nullptr) {
-                windowInfoProto->set_crop_layer_id(
-                        getLayerId(inputInfo->touchableRegionCropHandle.promote()));
-            } else {
-                windowInfoProto->set_crop_layer_id(-1);
-            }
+            windowInfoProto->set_crop_layer_id(
+                    mMapper->getLayerId(inputInfo->touchableRegionCropHandle.promote()));
         }
     }
     if (layer.what & layer_state_t::eBackgroundColorChanged) {
@@ -252,13 +265,10 @@
     return proto;
 }
 
-proto::DisplayState TransactionProtoParser::toProto(const DisplayState& display,
-                                                    DisplayHandleToIdFn getDisplayId) {
+proto::DisplayState TransactionProtoParser::toProto(const DisplayState& display) {
     proto::DisplayState proto;
     proto.set_what(display.what);
-    if (getDisplayId != nullptr) {
-        proto.set_id(getDisplayId(display.token));
-    }
+    proto.set_id(mMapper->getDisplayId(display.token));
 
     if (display.what & DisplayState::eLayerStackChanged) {
         proto.set_layer_stack(display.layerStack.id);
@@ -290,9 +300,7 @@
     return proto;
 }
 
-TransactionState TransactionProtoParser::fromProto(const proto::TransactionState& proto,
-                                                   LayerIdToHandleFn getLayerHandle,
-                                                   DisplayIdToHandleFn getDisplayHandle) {
+TransactionState TransactionProtoParser::fromProto(const proto::TransactionState& proto) {
     TransactionState t;
     t.originPid = proto.pid();
     t.originUid = proto.uid();
@@ -306,14 +314,14 @@
     for (int i = 0; i < layerCount; i++) {
         ComposerState s;
         s.state.what = 0;
-        fromProto(proto.layer_changes(i), getLayerHandle, s.state);
+        fromProto(proto.layer_changes(i), s.state);
         t.states.add(s);
     }
 
     int32_t displayCount = proto.display_changes_size();
     t.displays.reserve(static_cast<size_t>(displayCount));
     for (int i = 0; i < displayCount; i++) {
-        t.displays.add(fromProto(proto.display_changes(i), getDisplayHandle));
+        t.displays.add(fromProto(proto.display_changes(i)));
     }
     return t;
 }
@@ -328,10 +336,9 @@
 }
 
 void TransactionProtoParser::mergeFromProto(const proto::LayerState& proto,
-                                            LayerIdToHandleFn getLayerHandle,
                                             TracingLayerState& outState) {
     layer_state_t state;
-    fromProto(proto, getLayerHandle, state);
+    fromProto(proto, state);
     outState.merge(state);
 
     if (state.what & layer_state_t::eReparent) {
@@ -356,14 +363,10 @@
     }
 }
 
-void TransactionProtoParser::fromProto(const proto::LayerState& proto,
-                                       LayerIdToHandleFn getLayerHandle, layer_state_t& layer) {
+void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_state_t& layer) {
     layer.layerId = proto.layer_id();
     layer.what |= proto.what();
-
-    if (getLayerHandle != nullptr) {
-        layer.surface = getLayerHandle(layer.layerId);
-    }
+    layer.surface = mMapper->getLayerHandle(layer.layerId);
 
     if (proto.what() & layer_state_t::ePositionChanged) {
         layer.x = proto.x();
@@ -420,10 +423,11 @@
         LayerProtoHelper::readFromProto(proto.crop(), layer.crop);
     }
     if (proto.what() & layer_state_t::eBufferChanged) {
-        if (!layer.bufferData) {
-            layer.bufferData = std::make_shared<BufferData>();
-        }
         const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
+        layer.bufferData =
+                std::move(mMapper->getGraphicData(bufferProto.buffer_id(), bufferProto.width(),
+                                                  bufferProto.height(), bufferProto.pixel_format(),
+                                                  bufferProto.usage()));
         layer.bufferData->frameNumber = bufferProto.frame_number();
         layer.bufferData->flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
         layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
@@ -445,24 +449,24 @@
         }
     }
 
-    if ((proto.what() & layer_state_t::eReparent) && (getLayerHandle != nullptr)) {
+    if (proto.what() & layer_state_t::eReparent) {
         int32_t layerId = proto.parent_id();
         if (layerId == -1) {
             layer.parentSurfaceControlForChild = nullptr;
         } else {
             layer.parentSurfaceControlForChild =
-                    new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
-                                       nullptr, layerId);
+                    new SurfaceControl(SurfaceComposerClient::getDefault(),
+                                       mMapper->getLayerHandle(layerId), nullptr, layerId);
         }
     }
     if (proto.what() & layer_state_t::eRelativeLayerChanged) {
         int32_t layerId = proto.relative_parent_id();
         if (layerId == -1) {
             layer.relativeLayerSurfaceControl = nullptr;
-        } else if (getLayerHandle != nullptr) {
+        } else {
             layer.relativeLayerSurfaceControl =
-                    new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
-                                       nullptr, layerId);
+                    new SurfaceControl(SurfaceComposerClient::getDefault(),
+                                       mMapper->getLayerHandle(layerId), nullptr, layerId);
         }
         layer.z = proto.z();
     }
@@ -471,13 +475,17 @@
         gui::WindowInfo inputInfo;
         const proto::LayerState_WindowInfo& windowInfoProto = proto.window_info_handle();
 
-        inputInfo.flags = static_cast<gui::WindowInfo::Flag>(windowInfoProto.layout_params_flags());
-        inputInfo.type = static_cast<gui::WindowInfo::Type>(windowInfoProto.layout_params_type());
+        inputInfo.layoutParamsFlags =
+                static_cast<gui::WindowInfo::Flag>(windowInfoProto.layout_params_flags());
+        inputInfo.layoutParamsType =
+                static_cast<gui::WindowInfo::Type>(windowInfoProto.layout_params_type());
         LayerProtoHelper::readFromProto(windowInfoProto.touchable_region(),
                                         inputInfo.touchableRegion);
         inputInfo.surfaceInset = windowInfoProto.surface_inset();
-        inputInfo.focusable = windowInfoProto.focusable();
-        inputInfo.hasWallpaper = windowInfoProto.has_wallpaper();
+        inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_FOCUSABLE,
+                                 !windowInfoProto.focusable());
+        inputInfo.setInputConfig(gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER,
+                                 windowInfoProto.has_wallpaper());
         inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
         const proto::LayerState_Transform& transformProto = windowInfoProto.transform();
         inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
@@ -486,9 +494,7 @@
         inputInfo.replaceTouchableRegionWithCrop =
                 windowInfoProto.replace_touchable_region_with_crop();
         int32_t layerId = windowInfoProto.crop_layer_id();
-        if (getLayerHandle != nullptr) {
-            inputInfo.touchableRegionCropHandle = getLayerHandle(layerId);
-        }
+        inputInfo.touchableRegionCropHandle = mMapper->getLayerHandle(layerId);
         layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
     }
     if (proto.what() & layer_state_t::eBackgroundColorChanged) {
@@ -534,13 +540,10 @@
     }
 }
 
-DisplayState TransactionProtoParser::fromProto(const proto::DisplayState& proto,
-                                               DisplayIdToHandleFn getDisplayHandle) {
+DisplayState TransactionProtoParser::fromProto(const proto::DisplayState& proto) {
     DisplayState display;
     display.what = proto.what();
-    if (getDisplayHandle != nullptr) {
-        display.token = getDisplayHandle(proto.id());
-    }
+    display.token = mMapper->getDisplayHandle(proto.id());
 
     if (display.what & DisplayState::eLayerStackChanged) {
         display.layerStack.id = proto.layer_stack();
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index d589936..2f70b27 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -43,33 +43,77 @@
     TracingLayerCreationArgs args;
 };
 
-class TransactionProtoParser {
+// Class which exposes buffer properties from BufferData without holding on to the actual buffer
+// handle.
+class BufferDataStub : public BufferData {
 public:
-    typedef std::function<sp<IBinder>(int32_t)> LayerIdToHandleFn;
-    typedef std::function<sp<IBinder>(int32_t)> DisplayIdToHandleFn;
-    typedef std::function<int32_t(const sp<IBinder>&)> LayerHandleToIdFn;
-    typedef std::function<int32_t(const sp<IBinder>&)> DisplayHandleToIdFn;
-
-    static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn,
-                                           DisplayHandleToIdFn getDisplayIdFn);
-    static proto::TransactionState toProto(
-            const std::map<int32_t /* layerId */, TracingLayerState>&);
-
-    static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
-
-    static TransactionState fromProto(const proto::TransactionState&,
-                                      LayerIdToHandleFn getLayerHandleFn,
-                                      DisplayIdToHandleFn getDisplayHandleFn);
-    static void mergeFromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn,
-                               TracingLayerState& outState);
-    static void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+    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:
-    static proto::LayerState toProto(const layer_state_t&, LayerHandleToIdFn getLayerId);
-    static proto::DisplayState toProto(const DisplayState&, DisplayHandleToIdFn getDisplayId);
-    static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandle,
-                          layer_state_t& out);
-    static DisplayState fromProto(const proto::DisplayState&, DisplayIdToHandleFn getDisplayHandle);
+    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
+    // in SurfaceFlinger dependencies.
+    class FlingerDataMapper {
+    public:
+        virtual ~FlingerDataMapper() = default;
+        virtual sp<IBinder> getLayerHandle(int32_t /* layerId */) const { return nullptr; }
+        virtual int32_t getLayerId(const sp<IBinder>& /* layerHandle */) const { return -1; }
+        virtual sp<IBinder> getDisplayHandle(int32_t /* displayId */) const { return nullptr; }
+        virtual int32_t getDisplayId(const sp<IBinder>& /* displayHandle */) const { return -1; }
+        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);
+        }
+        virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */,
+                                                         uint64_t* /* outBufferId */,
+                                                         uint32_t* /* outWidth */,
+                                                         uint32_t* /* outHeight */,
+                                                         int32_t* /* outPixelFormat */,
+                                                         uint64_t* /* outUsage */) const {}
+    };
+
+    TransactionProtoParser(std::unique_ptr<FlingerDataMapper> provider)
+          : mMapper(std::move(provider)) {}
+
+    proto::TransactionState toProto(const TransactionState&);
+    proto::TransactionState toProto(const std::map<int32_t /* layerId */, TracingLayerState>&);
+    proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
+
+    TransactionState fromProto(const proto::TransactionState&);
+    void mergeFromProto(const proto::LayerState&, TracingLayerState& outState);
+    void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+
+private:
+    proto::LayerState toProto(const layer_state_t&);
+    proto::DisplayState toProto(const DisplayState&);
+    void fromProto(const proto::LayerState&, layer_state_t& out);
+    DisplayState fromProto(const proto::DisplayState&);
+
+    std::unique_ptr<FlingerDataMapper> mMapper;
 };
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index a46b795..d5e837f 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -23,11 +23,57 @@
 #include <utils/SystemClock.h>
 #include <utils/Trace.h>
 
+#include "ClientCache.h"
 #include "TransactionTracing.h"
+#include "renderengine/ExternalTexture.h"
 
 namespace android {
 
-TransactionTracing::TransactionTracing() {
+class FlingerDataMapper : public TransactionProtoParser::FlingerDataMapper {
+    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
+
+public:
+    FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
+          : mLayerHandles(layerHandles) {}
+
+    int32_t getLayerId(const sp<IBinder>& layerHandle) const override {
+        if (layerHandle == nullptr) {
+            return -1;
+        }
+        auto it = mLayerHandles.find(layerHandle->localBinder());
+        if (it == mLayerHandles.end()) {
+            ALOGW("Could not find layer handle %p", layerHandle->localBinder());
+            return -1;
+        }
+        return it->second;
+    }
+
+    void getGraphicBufferPropertiesFromCache(client_cache_t cachedBuffer, uint64_t* outBufferId,
+                                             uint32_t* outWidth, uint32_t* outHeight,
+                                             int32_t* outPixelFormat,
+                                             uint64_t* outUsage) const override {
+        std::shared_ptr<renderengine::ExternalTexture> buffer =
+                ClientCache::getInstance().get(cachedBuffer);
+        if (!buffer || !buffer->getBuffer()) {
+            *outBufferId = 0;
+            *outWidth = 0;
+            *outHeight = 0;
+            *outPixelFormat = 0;
+            *outUsage = 0;
+            return;
+        }
+
+        *outBufferId = buffer->getId();
+        *outWidth = buffer->getWidth();
+        *outHeight = buffer->getHeight();
+        *outPixelFormat = buffer->getPixelFormat();
+        *outUsage = buffer->getUsage();
+        return;
+    }
+};
+
+TransactionTracing::TransactionTracing()
+      : mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)) {
     std::scoped_lock lock(mTraceLock);
 
     mBuffer.setSize(mBufferSizeInBytes);
@@ -53,11 +99,11 @@
     writeToFile();
 }
 
-status_t TransactionTracing::writeToFile() {
+status_t TransactionTracing::writeToFile(std::string filename) {
     std::scoped_lock lock(mTraceLock);
     proto::TransactionTraceFile fileProto = createTraceFileProto();
     addStartingStateToProtoLocked(fileProto);
-    return mBuffer.writeToFile(fileProto, FILE_NAME);
+    return mBuffer.writeToFile(fileProto, filename);
 }
 
 void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
@@ -85,11 +131,7 @@
 void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
     std::scoped_lock lock(mTraceLock);
     ATRACE_CALL();
-    mQueuedTransactions[transaction.id] =
-            TransactionProtoParser::toProto(transaction,
-                                            std::bind(&TransactionTracing::getLayerIdLocked, this,
-                                                      std::placeholders::_1),
-                                            nullptr);
+    mQueuedTransactions[transaction.id] = mProtoParser.toProto(transaction);
 }
 
 void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
@@ -212,9 +254,7 @@
         ALOGW("Duplicate handles found. %p", layerHandle);
     }
     mLayerHandles[layerHandle] = layerId;
-    proto::LayerCreationArgs protoArgs = TransactionProtoParser::toProto(args);
-    proto::LayerCreationArgs protoArgsCopy = protoArgs;
-    mCreatedLayers.push_back(protoArgs);
+    mCreatedLayers.push_back(mProtoParser.toProto(args));
 }
 
 void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId,
@@ -225,7 +265,7 @@
         ALOGW("Duplicate handles found. %p", layerHandle);
     }
     mLayerHandles[layerHandle] = layerId;
-    mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args));
+    mCreatedLayers.emplace_back(mProtoParser.toProto(args));
 }
 
 void TransactionTracing::onLayerRemoved(int32_t layerId) {
@@ -263,18 +303,6 @@
     }
 }
 
-int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) {
-    if (layerHandle == nullptr) {
-        return -1;
-    }
-    auto it = mLayerHandles.find(layerHandle->localBinder());
-    if (it == mLayerHandles.end()) {
-        ALOGW("Could not find layer handle %p", layerHandle->localBinder());
-        return -1;
-    }
-    return it->second;
-}
-
 void TransactionTracing::updateStartingStateLocked(
         const proto::TransactionTraceEntry& removedEntry) {
     mStartingTimestamp = removedEntry.elapsed_realtime_nanos();
@@ -283,7 +311,7 @@
     for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
         TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()];
         startingState.layerId = addedLayer.layer_id();
-        TransactionProtoParser::fromProto(addedLayer, startingState.args);
+        mProtoParser.fromProto(addedLayer, startingState.args);
     }
 
     // Merge layer states to starting transaction state.
@@ -294,7 +322,7 @@
                 ALOGW("Could not find layer id %d", layerState.layer_id());
                 continue;
             }
-            TransactionProtoParser::mergeFromProto(layerState, nullptr, it->second);
+            mProtoParser.mergeFromProto(layerState, it->second);
         }
     }
 
@@ -316,10 +344,10 @@
 
     entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
     for (auto& [layerId, state] : mStartingStates) {
-        entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(state.args));
+        entryProto->mutable_added_layers()->Add(mProtoParser.toProto(state.args));
     }
 
-    proto::TransactionState transactionProto = TransactionProtoParser::toProto(mStartingStates);
+    proto::TransactionState transactionProto = mProtoParser.toProto(mStartingStates);
     transactionProto.set_vsync_id(0);
     transactionProto.set_post_time(mStartingTimestamp);
     entryProto->mutable_transactions()->Add(std::move(transactionProto));
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index d5d98ce..95256c4 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -55,7 +55,7 @@
 
     void addQueuedTransaction(const TransactionState&);
     void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
-    status_t writeToFile();
+    status_t writeToFile(std::string filename = FILE_NAME);
     void setBufferSize(size_t bufferSizeInBytes);
     void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
                       int parentId);
@@ -84,6 +84,7 @@
             GUARDED_BY(mTraceLock);
     std::vector<int32_t /* layerId */> mRemovedLayerHandles GUARDED_BY(mTraceLock);
     std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
+    TransactionProtoParser mProtoParser GUARDED_BY(mTraceLock);
 
     // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
     // otherwise will push data to temporary container.
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
new file mode 100644
index 0000000..114f1eb
--- /dev/null
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+cc_binary {
+    name: "layertracegenerator",
+    defaults: [
+        "libsurfaceflinger_mocks_defaults",
+        "surfaceflinger_defaults",
+        "skia_renderengine_deps",
+    ],
+    srcs: [
+        ":libsurfaceflinger_sources",
+        ":libsurfaceflinger_mock_sources",
+        ":layertracegenerator_sources",
+        "main.cpp",
+    ],
+    static_libs: [
+        "libgtest",
+    ],
+    header_libs: [
+        "libsurfaceflinger_mocks_headers",
+    ],
+}
+
+filegroup {
+    name: "layertracegenerator_sources",
+    srcs: [
+        "LayerTraceGenerator.cpp",
+    ],
+}
+
+cc_library_headers {
+    name: "layertracegenerator_headers",
+    export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
new file mode 100644
index 0000000..947fdce
--- /dev/null
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerTraceGenerator"
+
+#include <TestableSurfaceFlinger.h>
+#include <Tracing/TransactionProtoParser.h>
+#include <binder/IPCThreadState.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerState.h>
+#include <log/log.h>
+#include <mock/MockEventThread.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/FakeExternalTexture.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <utils/String16.h>
+#include <string>
+
+#include "LayerTraceGenerator.h"
+
+namespace android {
+
+class Factory final : public surfaceflinger::Factory {
+public:
+    ~Factory() = default;
+
+    std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; }
+
+    std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
+            Fps /*currentRefreshRate*/) override {
+        return std::make_unique<scheduler::FakePhaseOffsets>();
+    }
+
+    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
+        return new android::impl::SurfaceInterceptor();
+    }
+
+    sp<StartPropertySetThread> createStartPropertySetThread(
+            bool /* timestampPropertyValue */) override {
+        return nullptr;
+    }
+
+    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& /* creationArgs */) override {
+        return nullptr;
+    }
+
+    sp<GraphicBuffer> createGraphicBuffer(uint32_t /* width */, uint32_t /* height */,
+                                          PixelFormat /* format */, uint32_t /* layerCount */,
+                                          uint64_t /* usage */,
+                                          std::string /* requestorName */) override {
+        return nullptr;
+    }
+
+    void createBufferQueue(sp<IGraphicBufferProducer>* /* outProducer */,
+                           sp<IGraphicBufferConsumer>* /* outConsumer */,
+                           bool /* consumerIsSurfaceFlinger */) override {}
+
+    sp<IGraphicBufferProducer> createMonitoredProducer(
+            const sp<IGraphicBufferProducer>& /* producer */,
+            const sp<SurfaceFlinger>& /* flinger */, const wp<Layer>& /* layer */) override {
+        return nullptr;
+    }
+
+    sp<BufferLayerConsumer> createBufferLayerConsumer(
+            const sp<IGraphicBufferConsumer>& /* consumer */,
+            renderengine::RenderEngine& /* renderEngine */, uint32_t /* textureName */,
+            Layer* /* layer */) override {
+        return nullptr;
+    }
+
+    std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+            const sp<IGraphicBufferProducer>& /* producer */) override {
+        return nullptr;
+    }
+
+    std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
+        return compositionengine::impl::createCompositionEngine();
+    }
+
+    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) {
+        return sp<ContainerLayer>::make(args);
+    }
+
+    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) {
+        return new BufferStateLayer(args);
+    }
+
+    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) {
+        return new EffectLayer(args);
+    }
+
+    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override {
+        return nullptr;
+    }
+
+    std::unique_ptr<FrameTracer> createFrameTracer() override {
+        return std::make_unique<testing::NiceMock<mock::FrameTracer>>();
+    }
+
+    std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
+            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
+        return std::make_unique<testing::NiceMock<mock::FrameTimeline>>(timeStats,
+                                                                        surfaceFlingerPid);
+    }
+};
+
+class MockSurfaceFlinger : public SurfaceFlinger {
+public:
+    MockSurfaceFlinger(Factory& factory)
+          : SurfaceFlinger(factory, SurfaceFlinger::SkipInitialization) {}
+    std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
+            const BufferData& bufferData, const char* /* layerName */) const override {
+        return std::make_shared<renderengine::mock::FakeExternalTexture>(bufferData.getWidth(),
+                                                                         bufferData.getHeight(),
+                                                                         bufferData.getId(),
+                                                                         bufferData
+                                                                                 .getPixelFormat(),
+                                                                         bufferData.getUsage());
+    };
+
+    // b/220017192 migrate from transact codes to ISurfaceComposer apis
+    void setLayerTracingFlags(int32_t flags) {
+        Parcel data;
+        Parcel reply;
+        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+        data.writeInt32(flags);
+        transact(1033, data, &reply, 0 /* flags */);
+    }
+
+    void startLayerTracing(int64_t traceStartTime) {
+        Parcel data;
+        Parcel reply;
+        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+        data.writeInt32(1);
+        data.writeInt64(traceStartTime);
+        transact(1025, data, &reply, 0 /* flags */);
+    }
+
+    void stopLayerTracing(const char* tracePath) {
+        Parcel data;
+        Parcel reply;
+        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+        data.writeInt32(2);
+        data.writeCString(tracePath);
+        transact(1025, data, &reply, 0 /* flags */);
+    }
+};
+
+class TraceGenFlingerDataMapper : public TransactionProtoParser::FlingerDataMapper {
+public:
+    std::unordered_map<int32_t /*layerId*/, sp<IBinder> /* handle */> mLayerHandles;
+    sp<IBinder> getLayerHandle(int32_t layerId) const override {
+        if (layerId == -1) {
+            ALOGE("Error: Called with layer=%d", layerId);
+            return nullptr;
+        }
+        auto it = mLayerHandles.find(layerId);
+        if (it == mLayerHandles.end()) {
+            ALOGE("Error: Could not find handle for layer=%d", layerId);
+            return nullptr;
+        }
+        return it->second;
+    }
+};
+
+bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile,
+                                   const char* outputLayersTracePath) {
+    if (traceFile.entry_size() == 0) {
+        return false;
+    }
+
+    Factory mFactory;
+    sp<MockSurfaceFlinger> flinger = new MockSurfaceFlinger(mFactory);
+    TestableSurfaceFlinger mFlinger(flinger);
+    mFlinger.setupRenderEngine(
+            std::make_unique<testing::NiceMock<renderengine::mock::RenderEngine>>());
+    mock::VsyncController* mVsyncController = new testing::NiceMock<mock::VsyncController>();
+    mock::VSyncTracker* mVSyncTracker = new testing::NiceMock<mock::VSyncTracker>();
+    mock::EventThread* mEventThread = new testing::NiceMock<mock::EventThread>();
+    mock::EventThread* mSFEventThread = new testing::NiceMock<mock::EventThread>();
+    mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
+                            std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
+                            std::unique_ptr<EventThread>(mEventThread),
+                            std::unique_ptr<EventThread>(mSFEventThread),
+                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
+                            TestableSurfaceFlinger::kOneDisplayMode, true /* useNiceMock */);
+
+    Hwc2::mock::Composer* mComposer = new testing::NiceMock<Hwc2::mock::Composer>();
+    mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+    mFlinger.mutableMaxRenderTargetSize() = 16384;
+
+    flinger->setLayerTracingFlags(LayerTracing::TRACE_BUFFERS | LayerTracing::TRACE_INPUT |
+                                  LayerTracing::TRACE_BUFFERS);
+    flinger->startLayerTracing(traceFile.entry(0).elapsed_realtime_nanos());
+    std::unique_ptr<TraceGenFlingerDataMapper> mapper =
+            std::make_unique<TraceGenFlingerDataMapper>();
+    TraceGenFlingerDataMapper* dataMapper = mapper.get();
+    TransactionProtoParser parser(std::move(mapper));
+
+    nsecs_t frameTime;
+    int64_t vsyncId;
+    ALOGD("Generating %d transactions...", traceFile.entry_size());
+    for (int i = 0; i < traceFile.entry_size(); i++) {
+        proto::TransactionTraceEntry entry = traceFile.entry(i);
+        ALOGV("    Entry %04d/%04d for time=%" PRId64 " vsyncid=%" PRId64
+              " layers +%d -%d transactions=%d",
+              i, traceFile.entry_size(), entry.elapsed_realtime_nanos(), entry.vsync_id(),
+              entry.added_layers_size(), entry.removed_layers_size(), entry.transactions_size());
+
+        for (int j = 0; j < entry.added_layers_size(); j++) {
+            // create layers
+            TracingLayerCreationArgs tracingArgs;
+            parser.fromProto(entry.added_layers(j), tracingArgs);
+
+            sp<IBinder> outHandle;
+            int32_t outLayerId;
+            LayerCreationArgs args(mFlinger.flinger(), nullptr /* client */, tracingArgs.name,
+                                   tracingArgs.flags, LayerMetadata());
+            args.sequence = std::make_optional<int32_t>(tracingArgs.layerId);
+
+            if (tracingArgs.mirrorFromId == -1) {
+                sp<IBinder> parentHandle = nullptr;
+                if ((tracingArgs.parentId != -1) &&
+                    (dataMapper->mLayerHandles.find(tracingArgs.parentId) ==
+                     dataMapper->mLayerHandles.end())) {
+                    args.addToRoot = false;
+                } else {
+                    parentHandle = dataMapper->getLayerHandle(tracingArgs.parentId);
+                }
+                mFlinger.createLayer(args, &outHandle, parentHandle, &outLayerId,
+                                     nullptr /* parentLayer */, nullptr /* outTransformHint */);
+            } else {
+                sp<IBinder> mirrorFromHandle = dataMapper->getLayerHandle(tracingArgs.mirrorFromId);
+                mFlinger.mirrorLayer(args, mirrorFromHandle, &outHandle, &outLayerId);
+            }
+            LOG_ALWAYS_FATAL_IF(outLayerId != tracingArgs.layerId,
+                                "Could not create layer expected:%d actual:%d", tracingArgs.layerId,
+                                outLayerId);
+            dataMapper->mLayerHandles[tracingArgs.layerId] = outHandle;
+        }
+
+        for (int j = 0; j < entry.transactions_size(); j++) {
+            // apply transactions
+            TransactionState transaction = parser.fromProto(entry.transactions(j));
+            mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+                                         transaction.displays, transaction.flags,
+                                         transaction.applyToken, transaction.inputWindowCommands,
+                                         transaction.desiredPresentTime,
+                                         transaction.isAutoTimestamp, {},
+                                         transaction.hasListenerCallbacks,
+                                         transaction.listenerCallbacks, transaction.id);
+        }
+
+        for (int j = 0; j < entry.removed_layer_handles_size(); j++) {
+            dataMapper->mLayerHandles.erase(entry.removed_layer_handles(j));
+        }
+
+        frameTime = entry.elapsed_realtime_nanos();
+        vsyncId = entry.vsync_id();
+        mFlinger.commit(frameTime, vsyncId);
+    }
+
+    flinger->stopLayerTracing(outputLayersTracePath);
+    ALOGD("End of generating trace file. File written to %s", outputLayersTracePath);
+    dataMapper->mLayerHandles.clear();
+    return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/VsyncEventData.aidl b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
similarity index 66%
copy from libs/gui/aidl/android/gui/VsyncEventData.aidl
copy to services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
index 7343515..ee1ea6c 100644
--- a/libs/gui/aidl/android/gui/VsyncEventData.aidl
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 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,6 +14,13 @@
  * limitations under the License.
  */
 
-package android.gui;
+#pragma once
 
-parcelable VsyncEventData cpp_header "gui/VsyncEventData.h";
+#include <Tracing/TransactionTracing.h>
+
+namespace android {
+class LayerTraceGenerator {
+public:
+    bool generate(const proto::TransactionTraceFile&, const char* outputLayersTracePath);
+};
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
new file mode 100644
index 0000000..f3cf42d
--- /dev/null
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerTraceGenerator"
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "LayerTraceGenerator.h"
+
+using namespace android;
+
+int main(int argc, char** argv) {
+    if (argc > 3) {
+        std::cout << "Usage: " << argv[0]
+                  << " [transaction-trace-path] [output-layers-trace-path]\n";
+        return -1;
+    }
+
+    const char* transactionTracePath =
+            (argc > 1) ? argv[1] : "/data/misc/wmtrace/transactions_trace.winscope";
+    std::cout << "Parsing " << transactionTracePath << "\n";
+    std::fstream input(transactionTracePath, std::ios::in | std::ios::binary);
+    if (!input) {
+        std::cout << "Error: Could not open " << transactionTracePath;
+        return -1;
+    }
+
+    proto::TransactionTraceFile transactionTraceFile;
+    if (!transactionTraceFile.ParseFromIstream(&input)) {
+        std::cout << "Error: Failed to parse " << transactionTracePath;
+        return -1;
+    }
+
+    const char* outputLayersTracePath =
+            (argc == 3) ? argv[2] : "/data/misc/wmtrace/layers_trace.winscope";
+    ;
+    ALOGD("Generating %s...", outputLayersTracePath);
+    std::cout << "Generating " << outputLayersTracePath << "\n";
+    if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) {
+        std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
+        return -1;
+    }
+    return 0;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/Tracing/tools/readme.md b/services/surfaceflinger/Tracing/tools/readme.md
new file mode 100644
index 0000000..143c14f
--- /dev/null
+++ b/services/surfaceflinger/Tracing/tools/readme.md
@@ -0,0 +1,13 @@
+### LayerTraceGenerator ###
+
+Generates layer traces from transaction traces. The tool is a custom
+surface flinger build that mocks out everything else apart from the
+front end logic. Transaction traces are written when the transaction
+is applied, along wth a timestamp and vsync id. The transactions
+are parsed from proto and applied to recreate the layer state. The
+result is then written as a layer trace.
+
+Usage:
+1. build and push to device
+2. run ./layertracegenerator [transaction-trace-path] [output-layers-trace-path]
+
diff --git a/services/surfaceflinger/Tracing/tools/run.sh b/services/surfaceflinger/Tracing/tools/run.sh
new file mode 100644
index 0000000..baa93f1
--- /dev/null
+++ b/services/surfaceflinger/Tracing/tools/run.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+set -ex
+
+# Build, push and run layertracegenerator
+$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode layertracegenerator
+adb wait-for-device && adb push $OUT/system/bin/layertracegenerator /data/layertracegenerator
+echo "Writing transaction trace to file"
+adb shell service call SurfaceFlinger 1041 i32 0
+adb shell /data/layertracegenerator
+adb pull /data/misc/wmtrace/layers_trace.winscope
\ No newline at end of file
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index b705d9c..e1f348f 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -167,7 +167,7 @@
                                           handle->gpuCompositionDoneFence->getSnapshot().fence,
                                           handle->compositorTiming, handle->refreshStartTime,
                                           handle->dequeueReadyTime);
-        transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
+        transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTimeOrFence,
                                                     handle->previousReleaseFence,
                                                     handle->transformHint,
                                                     handle->currentMaxAcquiredBufferCount,
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 5ef5475..a68cd87 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -47,7 +47,7 @@
     std::string name;
     sp<Fence> previousReleaseFence;
     std::vector<std::shared_future<renderengine::RenderEngineResult>> previousReleaseFences;
-    nsecs_t acquireTime = -1;
+    std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
     nsecs_t latchTime = -1;
     uint32_t transformHint = 0;
     uint32_t currentMaxAcquiredBufferCount = 0;
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 23cd993..30b9d8f 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include "WindowInfosListenerInvoker.h"
+#include <ftl/small_vector.h>
 #include <gui/ISurfaceComposer.h>
-#include <unordered_set>
+
 #include "SurfaceFlinger.h"
+#include "WindowInfosListenerInvoker.h"
 
 namespace android {
 
@@ -41,18 +42,17 @@
       : mFlinger(flinger),
         mWindowInfosReportedListener(sp<WindowInfosReportedListener>::make(*this)) {}
 
-void WindowInfosListenerInvoker::addWindowInfosListener(
-        const sp<IWindowInfosListener>& windowInfosListener) {
-    sp<IBinder> asBinder = IInterface::asBinder(windowInfosListener);
-
+void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
+    sp<IBinder> asBinder = IInterface::asBinder(listener);
     asBinder->linkToDeath(this);
+
     std::scoped_lock lock(mListenersMutex);
-    mWindowInfosListeners.emplace(asBinder, windowInfosListener);
+    mWindowInfosListeners.try_emplace(asBinder, std::move(listener));
 }
 
 void WindowInfosListenerInvoker::removeWindowInfosListener(
-        const sp<IWindowInfosListener>& windowInfosListener) {
-    sp<IBinder> asBinder = IInterface::asBinder(windowInfosListener);
+        const sp<IWindowInfosListener>& listener) {
+    sp<IBinder> asBinder = IInterface::asBinder(listener);
 
     std::scoped_lock lock(mListenersMutex);
     asBinder->unlinkToDeath(this);
@@ -67,12 +67,11 @@
 void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
                                                     const std::vector<DisplayInfo>& displayInfos,
                                                     bool shouldSync) {
-    std::unordered_set<sp<IWindowInfosListener>, SpHash<IWindowInfosListener>> windowInfosListeners;
-
+    ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
     {
         std::scoped_lock lock(mListenersMutex);
         for (const auto& [_, listener] : mWindowInfosListeners) {
-            windowInfosListeners.insert(listener);
+            windowInfosListeners.push_back(listener);
         }
     }
 
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index 2eabf48..d8d8d0f 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -20,10 +20,8 @@
 #include <android/gui/IWindowInfosListener.h>
 #include <android/gui/IWindowInfosReportedListener.h>
 #include <binder/IBinder.h>
+#include <ftl/small_map.h>
 #include <utils/Mutex.h>
-#include <unordered_map>
-
-#include "WpHash.h"
 
 namespace android {
 
@@ -33,7 +31,7 @@
 public:
     explicit WindowInfosListenerInvoker(SurfaceFlinger&);
 
-    void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
+    void addWindowInfosListener(sp<gui::IWindowInfosListener>);
     void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
 
     void windowInfosChanged(const std::vector<gui::WindowInfo>&,
@@ -48,8 +46,11 @@
 
     SurfaceFlinger& mFlinger;
     std::mutex mListenersMutex;
-    std::unordered_map<wp<IBinder>, const sp<gui::IWindowInfosListener>, WpHash>
+
+    static constexpr size_t kStaticCapacity = 3;
+    ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
             mWindowInfosListeners GUARDED_BY(mListenersMutex);
+
     sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener;
     std::atomic<size_t> mCallbacksPending{0};
 };
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index b0d216e..7350e09 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -15,6 +15,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_defaults {
     name: "surfaceflinger_fuzz_defaults",
     include_dirs: [
@@ -102,3 +111,29 @@
         "surfaceflinger_scheduler_fuzzer.cpp",
     ],
 }
+
+cc_fuzz {
+    name: "surfaceflinger_layer_fuzzer",
+    defaults: [
+        "surfaceflinger_fuzz_defaults",
+    ],
+    header_libs: [
+        "libgui_headers",
+    ],
+    static_libs: [
+        "librenderengine",
+    ],
+    srcs: [
+        "surfaceflinger_layer_fuzzer.cpp",
+    ],
+}
+
+cc_fuzz {
+    name: "surfaceflinger_frametracer_fuzzer",
+    defaults: [
+        "surfaceflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "surfaceflinger_frametracer_fuzzer.cpp",
+    ],
+}
diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md
index 6231ca5..7a5f229 100644
--- a/services/surfaceflinger/fuzzer/README.md
+++ b/services/surfaceflinger/fuzzer/README.md
@@ -3,6 +3,8 @@
 + [SurfaceFlinger](#SurfaceFlinger)
 + [DisplayHardware](#DisplayHardware)
 + [Scheduler](#Scheduler)
++ [Layer](#Layer)
++ [FrameTracer](#FrameTracer)
 
 # <a name="SurfaceFlinger"></a> Fuzzer for SurfaceFlinger
 
@@ -70,3 +72,38 @@
   $ adb sync data
   $ adb shell /data/fuzz/arm64/surfaceflinger_scheduler_fuzzer/surfaceflinger_scheduler_fuzzer
 ```
+
+# <a name="Layer"></a> Fuzzer for Layer
+
+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`)
+
+You can find the possible values in the fuzzer's source code.
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) surfaceflinger_layer_fuzzer
+```
+2. Run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/surfaceflinger_layer_fuzzer/surfaceflinger_layer_fuzzer
+```
+
+# <a name="FrameTracer"></a> Fuzzer for FrameTracer
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) surfaceflinger_frametracer_fuzzer
+```
+2. To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/surfaceflinger_frametracer_fuzzer/surfaceflinger_frametracer_fuzzer
+```
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
new file mode 100644
index 0000000..a22a778
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <FrameTracer/FrameTracer.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <perfetto/trace/trace.pb.h>
+
+namespace android::fuzz {
+
+using namespace google::protobuf;
+
+constexpr size_t kMaxStringSize = 100;
+constexpr size_t kMinLayerIds = 1;
+constexpr size_t kMaxLayerIds = 10;
+constexpr int32_t kConfigDuration = 500;
+constexpr int32_t kBufferSize = 1024;
+constexpr int32_t kTimeOffset = 100000;
+
+class FrameTracerFuzzer {
+public:
+    FrameTracerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+        // Fuzzer is single-threaded, so no need to be thread-safe.
+        static bool wasInitialized = false;
+        if (!wasInitialized) {
+            perfetto::TracingInitArgs args;
+            args.backends = perfetto::kInProcessBackend;
+            perfetto::Tracing::Initialize(args);
+            wasInitialized = true;
+        }
+        mFrameTracer = std::make_unique<android::FrameTracer>();
+    }
+    ~FrameTracerFuzzer() { mFrameTracer.reset(); }
+    void process();
+
+private:
+    std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest();
+    void traceTimestamp();
+    std::vector<int32_t> generateLayerIds(size_t numLayerIds);
+    void traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds);
+    void traceFence(std::vector<int32_t> layerIds, size_t numLayerIds);
+    std::unique_ptr<android::FrameTracer> mFrameTracer = nullptr;
+    FuzzedDataProvider mFdp;
+    android::FenceToFenceTimeMap mFenceFactory;
+};
+
+std::unique_ptr<perfetto::TracingSession> FrameTracerFuzzer::getTracingSessionForTest() {
+    perfetto::TraceConfig cfg;
+    cfg.set_duration_ms(kConfigDuration);
+    cfg.add_buffers()->set_size_kb(kBufferSize);
+    auto* dsCfg = cfg.add_data_sources()->mutable_config();
+    dsCfg->set_name(android::FrameTracer::kFrameTracerDataSource);
+
+    auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+    tracingSession->Setup(cfg);
+    return tracingSession;
+}
+
+std::vector<int32_t> FrameTracerFuzzer::generateLayerIds(size_t numLayerIds) {
+    std::vector<int32_t> layerIds;
+    for (size_t i = 0; i < numLayerIds; ++i) {
+        layerIds.push_back(mFdp.ConsumeIntegral<int32_t>());
+    }
+    return layerIds;
+}
+
+void FrameTracerFuzzer::traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds) {
+    int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1));
+    mFrameTracer->traceTimestamp(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/,
+                                 mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/,
+                                 mFdp.ConsumeIntegral<nsecs_t>() /*timestamp*/,
+                                 android::FrameTracer::FrameEvent::UNSPECIFIED,
+                                 mFdp.ConsumeIntegral<nsecs_t>() /*duration*/);
+}
+
+void FrameTracerFuzzer::traceFence(std::vector<int32_t> layerIds, size_t numLayerIds) {
+    const nsecs_t signalTime = systemTime();
+    const nsecs_t startTime = signalTime + kTimeOffset;
+    auto fence = mFenceFactory.createFenceTimeForTest(android::Fence::NO_FENCE);
+    mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, signalTime);
+    int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1));
+    mFrameTracer->traceFence(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/,
+                             mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/, fence,
+                             android::FrameTracer::FrameEvent::ACQUIRE_FENCE, startTime);
+}
+
+void FrameTracerFuzzer::process() {
+    mFrameTracer->registerDataSource();
+
+    auto tracingSession = getTracingSessionForTest();
+    tracingSession->StartBlocking();
+
+    size_t numLayerIds = mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds);
+    std::vector<int32_t> layerIds = generateLayerIds(numLayerIds);
+
+    for (auto it = layerIds.begin(); it != layerIds.end(); ++it) {
+        mFrameTracer->traceNewLayer(*it /*layerId*/,
+                                    mFdp.ConsumeRandomLengthString(kMaxStringSize) /*layerName*/);
+    }
+
+    traceTimestamp(layerIds, numLayerIds);
+    traceFence(layerIds, numLayerIds);
+
+    mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, systemTime());
+
+    tracingSession->StopBlocking();
+
+    for (auto it = layerIds.begin(); it != layerIds.end(); ++it) {
+        mFrameTracer->onDestroy(*it);
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FrameTracerFuzzer frameTracerFuzzer(data, size);
+    frameTracerFuzzer.process();
+    return 0;
+}
+
+} // namespace android::fuzz
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index 4f89cd9..afc1abd 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -25,39 +25,10 @@
 
 static constexpr LatchUnsignaledConfig kLatchUnsignaledConfig[] = {
         LatchUnsignaledConfig::Always,
-        LatchUnsignaledConfig::Auto,
+        LatchUnsignaledConfig::AutoSingleLayer,
         LatchUnsignaledConfig::Disabled,
 };
 
-static constexpr ui::PixelFormat kPixelFormats[] = {ui::PixelFormat::RGBA_8888,
-                                                    ui::PixelFormat::RGBX_8888,
-                                                    ui::PixelFormat::RGB_888,
-                                                    ui::PixelFormat::RGB_565,
-                                                    ui::PixelFormat::BGRA_8888,
-                                                    ui::PixelFormat::YCBCR_422_SP,
-                                                    ui::PixelFormat::YCRCB_420_SP,
-                                                    ui::PixelFormat::YCBCR_422_I,
-                                                    ui::PixelFormat::RGBA_FP16,
-                                                    ui::PixelFormat::RAW16,
-                                                    ui::PixelFormat::BLOB,
-                                                    ui::PixelFormat::IMPLEMENTATION_DEFINED,
-                                                    ui::PixelFormat::YCBCR_420_888,
-                                                    ui::PixelFormat::RAW_OPAQUE,
-                                                    ui::PixelFormat::RAW10,
-                                                    ui::PixelFormat::RAW12,
-                                                    ui::PixelFormat::RGBA_1010102,
-                                                    ui::PixelFormat::Y8,
-                                                    ui::PixelFormat::Y16,
-                                                    ui::PixelFormat::YV12,
-                                                    ui::PixelFormat::DEPTH_16,
-                                                    ui::PixelFormat::DEPTH_24,
-                                                    ui::PixelFormat::DEPTH_24_STENCIL_8,
-                                                    ui::PixelFormat::DEPTH_32F,
-                                                    ui::PixelFormat::DEPTH_32F_STENCIL_8,
-                                                    ui::PixelFormat::STENCIL_8,
-                                                    ui::PixelFormat::YCBCR_P010,
-                                                    ui::PixelFormat::HSV_888};
-
 static constexpr ui::Rotation kRotations[] = {ui::Rotation::Rotation0, ui::Rotation::Rotation90,
                                               ui::Rotation::Rotation180, ui::Rotation::Rotation270};
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 0a458c2..2fcf856 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -123,6 +123,35 @@
                                                 ui::ColorMode::BT2100_HLG,
                                                 ui::ColorMode::DISPLAY_BT2020};
 
+static constexpr ui::PixelFormat kPixelFormats[] = {ui::PixelFormat::RGBA_8888,
+                                                    ui::PixelFormat::RGBX_8888,
+                                                    ui::PixelFormat::RGB_888,
+                                                    ui::PixelFormat::RGB_565,
+                                                    ui::PixelFormat::BGRA_8888,
+                                                    ui::PixelFormat::YCBCR_422_SP,
+                                                    ui::PixelFormat::YCRCB_420_SP,
+                                                    ui::PixelFormat::YCBCR_422_I,
+                                                    ui::PixelFormat::RGBA_FP16,
+                                                    ui::PixelFormat::RAW16,
+                                                    ui::PixelFormat::BLOB,
+                                                    ui::PixelFormat::IMPLEMENTATION_DEFINED,
+                                                    ui::PixelFormat::YCBCR_420_888,
+                                                    ui::PixelFormat::RAW_OPAQUE,
+                                                    ui::PixelFormat::RAW10,
+                                                    ui::PixelFormat::RAW12,
+                                                    ui::PixelFormat::RGBA_1010102,
+                                                    ui::PixelFormat::Y8,
+                                                    ui::PixelFormat::Y16,
+                                                    ui::PixelFormat::YV12,
+                                                    ui::PixelFormat::DEPTH_16,
+                                                    ui::PixelFormat::DEPTH_24,
+                                                    ui::PixelFormat::DEPTH_24_STENCIL_8,
+                                                    ui::PixelFormat::DEPTH_32F,
+                                                    ui::PixelFormat::DEPTH_32F_STENCIL_8,
+                                                    ui::PixelFormat::STENCIL_8,
+                                                    ui::PixelFormat::YCBCR_P010,
+                                                    ui::PixelFormat::HSV_888};
+
 FloatRect getFuzzedFloatRect(FuzzedDataProvider *fdp) {
     return FloatRect(fdp->ConsumeFloatingPoint<float>() /*left*/,
                      fdp->ConsumeFloatingPoint<float>() /*right*/,
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
new file mode 100644
index 0000000..46d52dd
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <BufferStateLayer.h>
+#include <Client.h>
+#include <DisplayDevice.h>
+#include <EffectLayer.h>
+#include <LayerRejecter.h>
+#include <LayerRenderArea.h>
+#include <MonitoredProducer.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <gui/IProducerListener.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/WindowInfo.h>
+#include <renderengine/mock/FakeExternalTexture.h>
+#include <ui/DisplayStatInfo.h>
+
+#include <FuzzableDataspaces.h>
+#include <surfaceflinger_fuzzers_utils.h>
+
+namespace android::fuzzer {
+using namespace renderengine;
+
+constexpr uint16_t kRandomStringLength = 256;
+
+class LayerFuzzer {
+public:
+    LayerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+    void init();
+    void invokeBufferStateLayer();
+    void invokeEffectLayer();
+    LayerCreationArgs createLayerCreationArgs(TestableSurfaceFlinger* flinger, sp<Client> client);
+    Rect getFuzzedRect();
+    FrameTimelineInfo getFuzzedFrameTimelineInfo();
+
+private:
+    FuzzedDataProvider mFdp;
+};
+
+Rect LayerFuzzer::getFuzzedRect() {
+    return Rect(mFdp.ConsumeIntegral<int32_t>() /*left*/, mFdp.ConsumeIntegral<int32_t>() /*top*/,
+                mFdp.ConsumeIntegral<int32_t>() /*right*/,
+                mFdp.ConsumeIntegral<int32_t>() /*bottom*/);
+}
+
+FrameTimelineInfo LayerFuzzer::getFuzzedFrameTimelineInfo() {
+    return FrameTimelineInfo{.vsyncId = mFdp.ConsumeIntegral<int64_t>(),
+                             .inputEventId = mFdp.ConsumeIntegral<int32_t>()};
+}
+
+LayerCreationArgs LayerFuzzer::createLayerCreationArgs(TestableSurfaceFlinger* flinger,
+                                                       sp<Client> client) {
+    flinger->setupScheduler(std::make_unique<android::mock::VsyncController>(),
+                            std::make_unique<android::mock::VSyncTracker>(),
+                            std::make_unique<android::mock::EventThread>(),
+                            std::make_unique<android::mock::EventThread>());
+
+    return LayerCreationArgs(flinger->flinger(), client,
+                             mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/,
+                             mFdp.ConsumeIntegral<uint32_t>() /*flags*/, {} /*metadata*/);
+}
+
+void LayerFuzzer::invokeEffectLayer() {
+    TestableSurfaceFlinger flinger;
+    sp<Client> client = sp<Client>::make(flinger.flinger());
+    const LayerCreationArgs layerCreationArgs = createLayerCreationArgs(&flinger, client);
+    sp<EffectLayer> effectLayer = sp<EffectLayer>::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);
+    effectLayer->setChildrenDrawingParent(parent);
+
+    const FrameTimelineInfo frameInfo = getFuzzedFrameTimelineInfo();
+    const int64_t postTime = mFdp.ConsumeIntegral<int64_t>();
+    effectLayer->setFrameTimelineVsyncForBufferTransaction(frameInfo, postTime);
+    effectLayer->setFrameTimelineVsyncForBufferlessTransaction(frameInfo, postTime);
+    auto surfaceFrame = effectLayer->createSurfaceFrameForTransaction(frameInfo, postTime);
+    auto surfaceFrame1 =
+            effectLayer->createSurfaceFrameForBuffer(frameInfo, postTime,
+                                                     mFdp.ConsumeRandomLengthString(
+                                                             kRandomStringLength) /*bufferName*/);
+    effectLayer->addSurfaceFramePresentedForBuffer(surfaceFrame,
+                                                   mFdp.ConsumeIntegral<int64_t>() /*acquireTime*/,
+                                                   mFdp.ConsumeIntegral<int64_t>() /*currentTime*/);
+    effectLayer->addSurfaceFrameDroppedForBuffer(surfaceFrame1);
+
+    parent.clear();
+    client.clear();
+    effectLayer.clear();
+}
+
+void LayerFuzzer::invokeBufferStateLayer() {
+    TestableSurfaceFlinger flinger;
+    sp<Client> client = sp<Client>::make(flinger.flinger());
+    sp<BufferStateLayer> layer =
+            sp<BufferStateLayer>::make(createLayerCreationArgs(&flinger, client));
+    sp<Fence> fence = sp<Fence>::make();
+    const std::shared_ptr<FenceTime> fenceTime = std::make_shared<FenceTime>(fence);
+
+    const CompositorTiming compositor = {mFdp.ConsumeIntegral<int64_t>(),
+                                         mFdp.ConsumeIntegral<int64_t>(),
+                                         mFdp.ConsumeIntegral<int64_t>()};
+    std::packaged_task<renderengine::RenderEngineResult()> renderResult([&] {
+        return renderengine::RenderEngineResult{mFdp.ConsumeIntegral<int32_t>(),
+                                                base::unique_fd(fence->get())};
+    });
+    layer->onLayerDisplayed(renderResult.get_future());
+    layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
+    layer->finalizeFrameEventHistory(fenceTime, compositor);
+    layer->onPostComposition(nullptr, fenceTime, fenceTime, compositor);
+    layer->isBufferDue(mFdp.ConsumeIntegral<int64_t>());
+
+    layer->setTransform(mFdp.ConsumeIntegral<uint32_t>());
+    layer->setTransformToDisplayInverse(mFdp.ConsumeBool());
+    layer->setCrop(getFuzzedRect());
+
+    layer->setHdrMetadata(getFuzzedHdrMetadata(&mFdp));
+    layer->setDataspace(mFdp.PickValueInArray(kDataspaces));
+    if (mFdp.ConsumeBool()) {
+        layer->setSurfaceDamageRegion(Region());
+        layer->setTransparentRegionHint(Region());
+    } else {
+        layer->setSurfaceDamageRegion(Region(getFuzzedRect()));
+        layer->setTransparentRegionHint(Region(getFuzzedRect()));
+    }
+    layer->setApi(mFdp.ConsumeIntegral<int32_t>());
+
+    native_handle_t* testHandle = native_handle_create(0, 1);
+    const bool ownsHandle = mFdp.ConsumeBool();
+    sp<NativeHandle> nativeHandle = sp<NativeHandle>::make(testHandle, ownsHandle);
+    layer->setSidebandStream(nativeHandle);
+    layer->addFrameEvent(fence, mFdp.ConsumeIntegral<int64_t>() /*postedTime*/,
+                         mFdp.ConsumeIntegral<int64_t>() /*requestedTime*/);
+    layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
+
+    layer->fenceHasSignaled();
+    layer->framePresentTimeIsCurrent(mFdp.ConsumeIntegral<int64_t>());
+    layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>());
+    const std::vector<sp<CallbackHandle>> callbacks;
+    layer->setTransactionCompletedListeners(callbacks);
+
+    std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
+            renderengine::mock::FakeExternalTexture>(mFdp.ConsumeIntegral<uint32_t>(),
+                                                     mFdp.ConsumeIntegral<uint32_t>(),
+                                                     mFdp.ConsumeIntegral<uint64_t>(),
+                                                     static_cast<android::PixelFormat>(
+                                                             mFdp.PickValueInArray(kPixelFormats)),
+                                                     mFdp.ConsumeIntegral<uint64_t>());
+    layer->setBuffer(texture, {} /*bufferData*/, mFdp.ConsumeIntegral<nsecs_t>() /*postTime*/,
+                     mFdp.ConsumeIntegral<nsecs_t>() /*desiredTime*/,
+                     mFdp.ConsumeBool() /*isAutoTimestamp*/,
+                     {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/);
+
+    LayerRenderArea layerArea(*(flinger.flinger()), layer, getFuzzedRect(),
+                              {mFdp.ConsumeIntegral<int32_t>(),
+                               mFdp.ConsumeIntegral<int32_t>()} /*reqSize*/,
+                              mFdp.PickValueInArray(kDataspaces), mFdp.ConsumeBool(),
+                              getFuzzedRect(), mFdp.ConsumeBool());
+    layerArea.render([]() {} /*drawLayers*/);
+
+    if (!ownsHandle) {
+        native_handle_close(testHandle);
+        native_handle_delete(testHandle);
+    }
+    nativeHandle.clear();
+    fence.clear();
+    client.clear();
+    layer.clear();
+}
+
+void LayerFuzzer::init() {
+    invokeBufferStateLayer();
+    invokeEffectLayer();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    LayerFuzzer layerFuzzer(data, size);
+    layerFuzzer.init();
+    return 0;
+}
+
+} // namespace android::fuzzer
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index efcc386..ceddf27 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -26,6 +26,7 @@
     defaults: ["surfaceflinger_defaults"],
     test_suites: ["device-tests"],
     srcs: [
+        "BootDisplayMode_test.cpp",
         "BufferGenerator.cpp",
         "CommonTypes_test.cpp",
         "Credentials_test.cpp",
diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
new file mode 100644
index 0000000..abdb16d
--- /dev/null
+++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <chrono>
+
+namespace android {
+
+TEST(BootDisplayModeTest, setBootDisplayMode) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    bool bootModeSupport = false;
+    ASSERT_NO_FATAL_FAILURE(sf->getBootDisplayModeSupport(&bootModeSupport));
+    if (bootModeSupport) {
+        ASSERT_EQ(NO_ERROR, sf->setBootDisplayMode(displayToken, 0));
+    }
+}
+
+TEST(BootDisplayModeTest, clearBootDisplayMode) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    bool bootModeSupport = false;
+    ASSERT_NO_FATAL_FAILURE(sf->getBootDisplayModeSupport(&bootModeSupport));
+    if (bootModeSupport) {
+        ASSERT_EQ(NO_ERROR, sf->clearBootDisplayMode(displayToken));
+    }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
index 01adbc8..0e54664 100644
--- a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
+++ b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
@@ -28,23 +28,24 @@
 
 TEST_F(DisplayEventReceiverTest, getLatestVsyncEventData) {
     const nsecs_t now = systemTime();
-    VsyncEventData vsyncEventData;
-    EXPECT_EQ(NO_ERROR, mDisplayEventReceiver.getLatestVsyncEventData(&vsyncEventData));
+    ParcelableVsyncEventData parcelableVsyncEventData;
+    EXPECT_EQ(NO_ERROR, mDisplayEventReceiver.getLatestVsyncEventData(&parcelableVsyncEventData));
 
+    const VsyncEventData& vsyncEventData = parcelableVsyncEventData.vsync;
     EXPECT_NE(std::numeric_limits<size_t>::max(), vsyncEventData.preferredFrameTimelineIndex);
     EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
             << "Deadline timestamp should be greater than frame time";
-    for (size_t i = 0; i < vsyncEventData.frameTimelines.size(); i++) {
-        EXPECT_NE(FrameTimelineInfo::INVALID_VSYNC_ID, vsyncEventData.frameTimelines[i].id);
-        EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentTime,
+    for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+        EXPECT_NE(FrameTimelineInfo::INVALID_VSYNC_ID, vsyncEventData.frameTimelines[i].vsyncId);
+        EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentationTime,
                   vsyncEventData.frameTimelines[i].deadlineTimestamp)
                 << "Expected vsync timestamp should be greater than deadline";
         if (i > 0) {
             EXPECT_GT(vsyncEventData.frameTimelines[i].deadlineTimestamp,
                       vsyncEventData.frameTimelines[i - 1].deadlineTimestamp)
                     << "Deadline timestamp out of order for frame timeline " << i;
-            EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentTime,
-                      vsyncEventData.frameTimelines[i - 1].expectedPresentTime)
+            EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentationTime,
+                      vsyncEventData.frameTimelines[i - 1].expectedPresentationTime)
                     << "Expected vsync timestamp out of order for frame timeline " << i;
         }
     }
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 5c16fee..8a2305b 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -141,11 +141,12 @@
                 continue;
             }
 
-            vsync = {event.vsync.vsyncId, event.vsync.expectedVSyncTimestamp};
+            vsync = {event.vsync.vsyncData.preferredVsyncId(),
+                     event.vsync.vsyncData.preferredExpectedPresentationTime()};
         }
 
         EXPECT_GE(vsync.vsyncId, 1);
-        EXPECT_GT(event.vsync.expectedVSyncTimestamp, systemTime());
+        EXPECT_GT(vsync.expectedPresentTime, systemTime());
 
         return vsync;
     }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 48f18b9..58cd7ba 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -42,6 +42,7 @@
 cc_test {
     name: "libsurfaceflinger_unittest",
     defaults: [
+        "libsurfaceflinger_mocks_defaults",
         "skia_renderengine_deps",
         "surfaceflinger_defaults",
     ],
@@ -80,6 +81,7 @@
         "FpsReporterTest.cpp",
         "FpsTest.cpp",
         "FramebufferSurfaceTest.cpp",
+        "FrameRateOverrideMappingsTest.cpp",
         "FrameTimelineTest.cpp",
         "GameModeTest.cpp",
         "HWComposerTest.cpp",
@@ -121,6 +123,10 @@
         "VSyncReactorTest.cpp",
         "VsyncConfigurationTest.cpp",
     ],
+}
+
+cc_defaults {
+    name: "libsurfaceflinger_mocks_defaults",
     static_libs: [
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
@@ -192,3 +198,8 @@
         "libsurfaceflinger_headers",
     ],
 }
+
+cc_library_headers {
+    name: "libsurfaceflinger_mocks_headers",
+    export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 0b6b475..ec27eda 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -229,7 +229,7 @@
         ASSERT_TRUE(callbackData.has_value());
         const auto [when, vsyncData] = callbackData.value();
         EXPECT_EQ(when,
-                  vsyncData.expectedVSyncTimestamp - mWorkDuration.count() -
+                  vsyncData.expectedPresentationTime - mWorkDuration.count() -
                           mReadyDuration.count());
     }
 }
@@ -261,7 +261,7 @@
         ASSERT_TRUE(callbackData.has_value());
         const auto [when, vsyncData] = callbackData.value();
         EXPECT_EQ(when,
-                  vsyncData.expectedVSyncTimestamp - mWorkDuration.count() -
+                  vsyncData.expectedPresentationTime - mWorkDuration.count() -
                           mReadyDuration.count());
     }
 
@@ -283,7 +283,7 @@
         const auto callbackData = mVSyncEventCallRecorder.waitForCall();
         ASSERT_TRUE(callbackData.has_value());
         const auto [when, vsyncData] = callbackData.value();
-        EXPECT_EQ(when, vsyncData.expectedVSyncTimestamp - newDuration.count());
+        EXPECT_EQ(when, vsyncData.expectedPresentationTime - newDuration.count());
     }
 
     EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
@@ -307,9 +307,9 @@
 
     const auto vsyncData = mDispSyncSource->getLatestVSyncData();
     ASSERT_GT(vsyncData.deadlineTimestamp, now);
-    ASSERT_GT(vsyncData.expectedVSyncTimestamp, vsyncData.deadlineTimestamp);
+    ASSERT_GT(vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp);
     EXPECT_EQ(vsyncData.deadlineTimestamp,
-              vsyncData.expectedVSyncTimestamp - vsyncInternalDuration);
+              vsyncData.expectedPresentationTime - vsyncInternalDuration);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index cc0a40f..14d8f98 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -99,7 +99,7 @@
                                               nsecs_t expectedTimestamp, unsigned expectedCount);
     void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
     void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
-                                               nsecs_t preferredDeadline);
+                                               VSyncSource::VSyncData preferredVsyncData);
     void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                 bool expectedConnected);
     void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -252,40 +252,42 @@
                                          expectedCount);
 }
 
-void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
-                                                            nsecs_t preferredDeadline) {
+void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(
+        nsecs_t expectedTimestamp, VSyncSource::VSyncData preferredVsyncData) {
     auto args = mConnectionEventCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
                                   << expectedTimestamp;
     const auto& event = std::get<0>(args.value());
     for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
-        auto prediction =
-                mTokenManager->getPredictionsForToken(event.vsync.frameTimelines[i].vsyncId);
+        auto prediction = mTokenManager->getPredictionsForToken(
+                event.vsync.vsyncData.frameTimelines[i].vsyncId);
         EXPECT_TRUE(prediction.has_value());
-        EXPECT_EQ(prediction.value().endTime, event.vsync.frameTimelines[i].deadlineTimestamp)
+        EXPECT_EQ(prediction.value().endTime,
+                  event.vsync.vsyncData.frameTimelines[i].deadlineTimestamp)
                 << "Deadline timestamp does not match cached value";
         EXPECT_EQ(prediction.value().presentTime,
-                  event.vsync.frameTimelines[i].expectedVSyncTimestamp)
-                << "Expected vsync timestamp does not match cached value";
+                  event.vsync.vsyncData.frameTimelines[i].expectedPresentationTime)
+                << "Expected vsync.vsyncData timestamp does not match cached value";
 
         if (i > 0) {
-            EXPECT_GT(event.vsync.frameTimelines[i].deadlineTimestamp,
-                      event.vsync.frameTimelines[i - 1].deadlineTimestamp)
+            EXPECT_GT(event.vsync.vsyncData.frameTimelines[i].deadlineTimestamp,
+                      event.vsync.vsyncData.frameTimelines[i - 1].deadlineTimestamp)
                     << "Deadline timestamp out of order for frame timeline " << i;
-            EXPECT_GT(event.vsync.frameTimelines[i].expectedVSyncTimestamp,
-                      event.vsync.frameTimelines[i - 1].expectedVSyncTimestamp)
-                    << "Expected vsync timestamp out of order for frame timeline " << i;
+            EXPECT_GT(event.vsync.vsyncData.frameTimelines[i].expectedPresentationTime,
+                      event.vsync.vsyncData.frameTimelines[i - 1].expectedPresentationTime)
+                    << "Expected vsync.vsyncData timestamp out of order for frame timeline " << i;
         }
 
         // Vsync ID order lines up with registration into test token manager.
-        EXPECT_EQ(i, event.vsync.frameTimelines[i].vsyncId)
+        EXPECT_EQ(i, event.vsync.vsyncData.frameTimelines[i].vsyncId)
                 << "Vsync ID incorrect for frame timeline " << i;
-        if (i == event.vsync.preferredFrameTimelineIndex) {
-            EXPECT_EQ(event.vsync.frameTimelines[i].deadlineTimestamp, preferredDeadline)
+        if (i == event.vsync.vsyncData.preferredFrameTimelineIndex) {
+            EXPECT_EQ(event.vsync.vsyncData.frameTimelines[i].deadlineTimestamp,
+                      preferredVsyncData.deadlineTimestamp)
                     << "Preferred deadline timestamp incorrect" << i;
-            EXPECT_EQ(event.vsync.frameTimelines[i].expectedVSyncTimestamp,
-                      event.vsync.expectedVSyncTimestamp)
-                    << "Preferred expected vsync timestamp incorrect" << i;
+            EXPECT_EQ(event.vsync.vsyncData.frameTimelines[i].expectedPresentationTime,
+                      preferredVsyncData.expectedPresentationTime)
+                    << "Preferred expected vsync.vsyncData timestamp incorrect" << i;
         }
     }
 }
@@ -397,16 +399,17 @@
 
     // Use the received callback to signal a vsync event.
     // The interceptor should receive the event, as well as the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
+    VSyncSource::VSyncData vsyncData = {456, 789};
+    mCallback->onVSyncEvent(123, vsyncData);
     expectInterceptCallReceived(123);
-    expectVsyncEventFrameTimelinesCorrect(123, 789);
+    expectVsyncEventFrameTimelinesCorrect(123, vsyncData);
 }
 
 TEST_F(EventThreadTest, getLatestVsyncEventData) {
     const nsecs_t now = systemTime();
     const nsecs_t preferredDeadline = now + 10000000;
-    const nsecs_t preferredExpectedVSyncTimestamp = now + 20000000;
-    const VSyncSource::VSyncData preferredData = {preferredExpectedVSyncTimestamp,
+    const nsecs_t preferredExpectedPresentationTime = now + 20000000;
+    const VSyncSource::VSyncData preferredData = {preferredExpectedPresentationTime,
                                                   preferredDeadline};
     EXPECT_CALL(*mVSyncSource, getLatestVSyncData()).WillOnce(Return(preferredData));
 
@@ -415,14 +418,14 @@
             << "Deadline timestamp should be greater than frame time";
     for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
         auto prediction =
-                mTokenManager->getPredictionsForToken(vsyncEventData.frameTimelines[i].id);
+                mTokenManager->getPredictionsForToken(vsyncEventData.frameTimelines[i].vsyncId);
         EXPECT_TRUE(prediction.has_value());
         EXPECT_EQ(prediction.value().endTime, vsyncEventData.frameTimelines[i].deadlineTimestamp)
                 << "Deadline timestamp does not match cached value";
         EXPECT_EQ(prediction.value().presentTime,
-                  vsyncEventData.frameTimelines[i].expectedPresentTime)
+                  vsyncEventData.frameTimelines[i].expectedPresentationTime)
                 << "Expected vsync timestamp does not match cached value";
-        EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentTime,
+        EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentationTime,
                   vsyncEventData.frameTimelines[i].deadlineTimestamp)
                 << "Expected vsync timestamp should be greater than deadline";
 
@@ -430,19 +433,19 @@
             EXPECT_GT(vsyncEventData.frameTimelines[i].deadlineTimestamp,
                       vsyncEventData.frameTimelines[i - 1].deadlineTimestamp)
                     << "Deadline timestamp out of order for frame timeline " << i;
-            EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentTime,
-                      vsyncEventData.frameTimelines[i - 1].expectedPresentTime)
+            EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentationTime,
+                      vsyncEventData.frameTimelines[i - 1].expectedPresentationTime)
                     << "Expected vsync timestamp out of order for frame timeline " << i;
         }
 
         // Vsync ID order lines up with registration into test token manager.
-        EXPECT_EQ(i, vsyncEventData.frameTimelines[i].id)
+        EXPECT_EQ(i, vsyncEventData.frameTimelines[i].vsyncId)
                 << "Vsync ID incorrect for frame timeline " << i;
         if (i == vsyncEventData.preferredFrameTimelineIndex) {
             EXPECT_EQ(vsyncEventData.frameTimelines[i].deadlineTimestamp, preferredDeadline)
                     << "Preferred deadline timestamp incorrect" << i;
-            EXPECT_EQ(vsyncEventData.frameTimelines[i].expectedPresentTime,
-                      preferredExpectedVSyncTimestamp)
+            EXPECT_EQ(vsyncEventData.frameTimelines[i].expectedPresentationTime,
+                      preferredExpectedPresentationTime)
                     << "Preferred expected vsync timestamp incorrect" << i;
         }
     }
diff --git a/services/surfaceflinger/tests/unittests/FpsTest.cpp b/services/surfaceflinger/tests/unittests/FpsTest.cpp
index 88b74d2..2193c9d 100644
--- a/services/surfaceflinger/tests/unittests/FpsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsTest.cpp
@@ -68,4 +68,14 @@
     EXPECT_EQ(31, (30.5_Hz).getIntValue());
 }
 
+TEST(FpsTest, range) {
+    const auto fps = Fps::fromPeriodNsecs(16'666'665);
+
+    EXPECT_TRUE((FpsRange{60.000004_Hz, 60.000004_Hz}.includes(fps)));
+    EXPECT_TRUE((FpsRange{59_Hz, 60.1_Hz}.includes(fps)));
+    EXPECT_FALSE((FpsRange{75_Hz, 90_Hz}.includes(fps)));
+    EXPECT_FALSE((FpsRange{60.0011_Hz, 90_Hz}.includes(fps)));
+    EXPECT_FALSE((FpsRange{50_Hz, 59.998_Hz}.includes(fps)));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp
new file mode 100644
index 0000000..a581c7a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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 "FrameRateOverrideMappingsTest"
+
+#include <gtest/gtest.h>
+#include <unordered_map>
+
+#include "Scheduler/FrameRateOverrideMappings.h"
+
+namespace android::scheduler {
+
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+using UidToFrameRateOverride = std::map<uid_t, Fps>;
+
+class FrameRateOverrideMappingsTest : public testing::Test {
+protected:
+    FrameRateOverrideMappings mFrameRateOverrideMappings;
+    UidToFrameRateOverride mFrameRateOverrideByContent;
+};
+
+namespace {
+TEST_F(FrameRateOverrideMappingsTest, testUpdateFrameRateOverridesByContent) {
+    mFrameRateOverrideByContent.clear();
+    mFrameRateOverrideByContent.emplace(0, 30.0_Hz);
+    mFrameRateOverrideByContent.emplace(1, 60.0_Hz);
+    ASSERT_TRUE(mFrameRateOverrideMappings.updateFrameRateOverridesByContent(
+            mFrameRateOverrideByContent));
+
+    ASSERT_TRUE(isApproxEqual(30.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      0, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_TRUE(isApproxEqual(60.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      1, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_EQ(std::nullopt,
+              mFrameRateOverrideMappings
+                      .getFrameRateOverrideForUid(1, /*supportsFrameRateOverrideByContent*/ false));
+    ASSERT_EQ(std::nullopt,
+              mFrameRateOverrideMappings
+                      .getFrameRateOverrideForUid(3, /*supportsFrameRateOverrideByContent*/ true));
+    ASSERT_EQ(std::nullopt,
+              mFrameRateOverrideMappings
+                      .getFrameRateOverrideForUid(3, /*supportsFrameRateOverrideByContent*/ false));
+}
+
+TEST_F(FrameRateOverrideMappingsTest, testSetGameModeRefreshRateForUid) {
+    mFrameRateOverrideMappings.setGameModeRefreshRateForUid({1, 30.0f});
+    mFrameRateOverrideMappings.setGameModeRefreshRateForUid({2, 90.0f});
+
+    ASSERT_TRUE(isApproxEqual(30.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      1, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_TRUE(isApproxEqual(90.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      2, /*supportsFrameRateOverrideByContent*/ false)));
+    ASSERT_EQ(std::nullopt,
+              mFrameRateOverrideMappings
+                      .getFrameRateOverrideForUid(0, /*supportsFrameRateOverrideByContent*/ true));
+    ASSERT_EQ(std::nullopt,
+              mFrameRateOverrideMappings
+                      .getFrameRateOverrideForUid(0, /*supportsFrameRateOverrideByContent*/ false));
+}
+
+TEST_F(FrameRateOverrideMappingsTest, testSetPreferredRefreshRateForUid) {
+    mFrameRateOverrideMappings.setPreferredRefreshRateForUid({0, 60.0f});
+    mFrameRateOverrideMappings.setPreferredRefreshRateForUid({2, 120.0f});
+
+    ASSERT_TRUE(isApproxEqual(60.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      0, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_TRUE(isApproxEqual(120.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      2, /*supportsFrameRateOverrideByContent*/ false)));
+    ASSERT_EQ(std::nullopt,
+              mFrameRateOverrideMappings
+                      .getFrameRateOverrideForUid(1, /*supportsFrameRateOverrideByContent*/ true));
+    ASSERT_EQ(std::nullopt,
+              mFrameRateOverrideMappings
+                      .getFrameRateOverrideForUid(1, /*supportsFrameRateOverrideByContent*/ false));
+}
+
+TEST_F(FrameRateOverrideMappingsTest, testGetFrameRateOverrideForUidMixed) {
+    mFrameRateOverrideByContent.clear();
+    mFrameRateOverrideByContent.emplace(0, 30.0_Hz);
+    mFrameRateOverrideByContent.emplace(1, 60.0_Hz);
+    mFrameRateOverrideByContent.emplace(2, 45.0_Hz);
+    mFrameRateOverrideByContent.emplace(5, 120.0_Hz);
+    ASSERT_TRUE(mFrameRateOverrideMappings.updateFrameRateOverridesByContent(
+            mFrameRateOverrideByContent));
+
+    std::vector<FrameRateOverride> allFrameRateOverrides;
+    ASSERT_EQ(allFrameRateOverrides,
+              mFrameRateOverrideMappings.getAllFrameRateOverrides(
+                      /*supportsFrameRateOverrideByContent*/ false));
+    allFrameRateOverrides = {{0, 30.0f}, {1, 60.0f}, {2, 45.0f}, {5, 120.0f}};
+    ASSERT_EQ(allFrameRateOverrides,
+              mFrameRateOverrideMappings.getAllFrameRateOverrides(
+                      /*supportsFrameRateOverrideByContent*/ true));
+
+    mFrameRateOverrideMappings.setGameModeRefreshRateForUid({1, 30.0f});
+    mFrameRateOverrideMappings.setGameModeRefreshRateForUid({2, 90.0f});
+    mFrameRateOverrideMappings.setGameModeRefreshRateForUid({4, 120.0f});
+
+    allFrameRateOverrides.clear();
+    allFrameRateOverrides = {{1, 30.0f}, {2, 90.0f}, {4, 120.0f}};
+    ASSERT_EQ(allFrameRateOverrides,
+              mFrameRateOverrideMappings.getAllFrameRateOverrides(
+                      /*supportsFrameRateOverrideByContent*/ false));
+    allFrameRateOverrides.clear();
+    allFrameRateOverrides = {{1, 30.0f}, {2, 90.0f}, {4, 120.0f}, {0, 30.0f}, {5, 120.0f}};
+    ASSERT_EQ(allFrameRateOverrides,
+              mFrameRateOverrideMappings.getAllFrameRateOverrides(
+                      /*supportsFrameRateOverrideByContent*/ true));
+
+    mFrameRateOverrideMappings.setPreferredRefreshRateForUid({0, 60.0f});
+    mFrameRateOverrideMappings.setPreferredRefreshRateForUid({2, 120.0f});
+    mFrameRateOverrideMappings.setPreferredRefreshRateForUid({3, 30.0f});
+
+    allFrameRateOverrides.clear();
+    allFrameRateOverrides = {{0, 60.0f}, {2, 120.0f}, {3, 30.0f}, {1, 30.0f}, {4, 120.0f}};
+    ASSERT_EQ(allFrameRateOverrides,
+              mFrameRateOverrideMappings.getAllFrameRateOverrides(
+                      /*supportsFrameRateOverrideByContent*/ false));
+    allFrameRateOverrides.clear();
+    allFrameRateOverrides = {{0, 60.0f}, {2, 120.0f}, {3, 30.0f},
+                             {1, 30.0f}, {4, 120.0f}, {5, 120.0f}};
+    ASSERT_EQ(allFrameRateOverrides,
+              mFrameRateOverrideMappings.getAllFrameRateOverrides(
+                      /*supportsFrameRateOverrideByContent*/ true));
+
+    ASSERT_TRUE(isApproxEqual(60.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      0, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_TRUE(isApproxEqual(30.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      1, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_TRUE(isApproxEqual(120.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      2, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_TRUE(isApproxEqual(30.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      3, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_TRUE(isApproxEqual(120.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      4, /*supportsFrameRateOverrideByContent*/ true)));
+    ASSERT_TRUE(isApproxEqual(120.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      5, /*supportsFrameRateOverrideByContent*/ true)));
+
+    ASSERT_TRUE(isApproxEqual(60.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      0, /*supportsFrameRateOverrideByContent*/ false)));
+    ASSERT_TRUE(isApproxEqual(30.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      1, /*supportsFrameRateOverrideByContent*/ false)));
+    ASSERT_TRUE(isApproxEqual(120.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      2, /*supportsFrameRateOverrideByContent*/ false)));
+    ASSERT_TRUE(isApproxEqual(30.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      3, /*supportsFrameRateOverrideByContent*/ false)));
+    ASSERT_TRUE(isApproxEqual(120.0_Hz,
+                              *mFrameRateOverrideMappings.getFrameRateOverrideForUid(
+                                      4, /*supportsFrameRateOverrideByContent*/ false)));
+    ASSERT_EQ(std::nullopt,
+              mFrameRateOverrideMappings
+                      .getFrameRateOverrideForUid(5, /*supportsFrameRateOverrideByContent*/ false));
+}
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 397c619..834a560 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -1279,7 +1279,7 @@
     validateTraceEvent(actualSurfaceFrameEnd2, protoPresentedSurfaceFrameActualEnd);
 }
 
-TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDoesNotTraceExpectedTimeline) {
+TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredIsAppMissedDeadline) {
     auto tracingSession = getTracingSessionForTest();
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -1312,7 +1312,7 @@
             createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
                                                displayFrameToken, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
-                                               false, FrameTimelineEvent::JANK_UNKNOWN,
+                                               false, FrameTimelineEvent::JANK_APP_DEADLINE_MISSED,
                                                FrameTimelineEvent::PREDICTION_EXPIRED, true);
     auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
 
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index cdb2240..e108bea 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -97,7 +97,7 @@
     void setDefaultLayerVote(Layer* layer,
                              LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
         auto [found, layerPair] = history().findLayer(layer->getSequence());
-        if (found != LayerHistory::layerStatus::NotFound) {
+        if (found != LayerHistory::LayerStatus::NotFound) {
             layerPair->second->setDefaultLayerVote(vote);
         }
     }
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 4efcc05..9143d61 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -834,17 +834,6 @@
     EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, testInPolicy) {
-    const auto refreshRate =
-            asRefreshRate(createDisplayMode(kModeId60, Fps::fromPeriodNsecs(16'666'665)));
-
-    EXPECT_TRUE(refreshRate.inPolicy(60.000004_Hz, 60.000004_Hz));
-    EXPECT_TRUE(refreshRate.inPolicy(59_Hz, 60.1_Hz));
-    EXPECT_FALSE(refreshRate.inPolicy(75_Hz, 90_Hz));
-    EXPECT_FALSE(refreshRate.inPolicy(60.0011_Hz, 90_Hz));
-    EXPECT_FALSE(refreshRate.inPolicy(50_Hz, 59.998_Hz));
-}
-
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index f48abb7..a992a91 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -228,7 +228,8 @@
     mScheduler->setRefreshRateConfigs(
             std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
 
-    sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+    const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+    EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
 
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
 
@@ -240,6 +241,10 @@
 
     EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1);
     mScheduler->chooseRefreshRateForContent();
+
+    // No-op if layer requirements have not changed.
+    EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
+    mScheduler->chooseRefreshRateForContent();
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8cadb31..d83b9bb 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -177,8 +177,10 @@
 public:
     using HotplugEvent = SurfaceFlinger::HotplugEvent;
 
-    TestableSurfaceFlinger()
-          : mFlinger(sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization)) {
+    TestableSurfaceFlinger(sp<SurfaceFlinger> flinger = nullptr) : mFlinger(flinger) {
+        if (!mFlinger) {
+            mFlinger = sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
+        }
         mFlinger->mAnimationTransactionTimeout = ms2ns(10);
     }
 
@@ -219,7 +221,8 @@
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
                         SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
-                        DisplayModesVariant modesVariant = kOneDisplayMode) {
+                        DisplayModesVariant modesVariant = kOneDisplayMode,
+                        bool useNiceMock = false) {
         RefreshRateConfigsPtr configs;
         if (std::holds_alternative<RefreshRateConfigsPtr>(modesVariant)) {
             configs = std::move(std::get<RefreshRateConfigsPtr>(modesVariant));
@@ -256,9 +259,17 @@
                 ? static_cast<Callback&>(mNoOpSchedulerCallback)
                 : static_cast<Callback&>(mSchedulerCallback);
 
-        mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
-                                                      std::move(vsyncTracker), std::move(configs),
-                                                      callback);
+        if (useNiceMock) {
+            mScheduler =
+                    new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController),
+                                                                        std::move(vsyncTracker),
+                                                                        std::move(configs),
+                                                                        callback);
+        } else {
+            mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
+                                                          std::move(vsyncTracker),
+                                                          std::move(configs), callback);
+        }
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -270,6 +281,8 @@
     scheduler::TestableScheduler& mutableScheduler() { return *mScheduler; }
     scheduler::mock::SchedulerCallback& mockSchedulerCallback() { return mSchedulerCallback; }
 
+    auto& mutableVsyncModulator() { return mFlinger->mVsyncModulator; }
+
     using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
         mFactory.mCreateBufferQueue = f;
@@ -453,6 +466,23 @@
         mFlinger->onActiveDisplayChangedLocked(activeDisplay);
     }
 
+    auto commit(nsecs_t frameTime, int64_t vsyncId) {
+        const nsecs_t expectedVsyncTime = frameTime + 10'000'000;
+        mFlinger->commit(frameTime, vsyncId, expectedVsyncTime);
+    }
+
+    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 mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
+                     sp<IBinder>* outHandle, int32_t* outLayerId) {
+        return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -848,8 +878,7 @@
 
 private:
     surfaceflinger::test::Factory mFactory;
-    sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
-
+    sp<SurfaceFlinger> mFlinger;
     scheduler::mock::SchedulerCallback mSchedulerCallback;
     scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
     scheduler::TestableScheduler* mScheduler = nullptr;
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index ed23176..eefa11f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -72,13 +72,6 @@
         EXPECT_CALL(*mVSyncTracker, currentPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
 
-        EXPECT_CALL(*mFenceUnsignaled, getStatus())
-                .WillRepeatedly(Return(Fence::Status::Unsignaled));
-        EXPECT_CALL(*mFenceUnsignaled2, getStatus())
-                .WillRepeatedly(Return(Fence::Status::Unsignaled));
-        EXPECT_CALL(*mFenceSignaled, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
-        EXPECT_CALL(*mFenceSignaled2, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
-
         mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
         mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
                                 std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
@@ -89,10 +82,6 @@
 
     mock::VsyncController* mVsyncController = new mock::VsyncController();
     mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
-    sp<mock::MockFence> mFenceUnsignaled = sp<mock::MockFence>::make();
-    sp<mock::MockFence> mFenceSignaled = sp<mock::MockFence>::make();
-    sp<mock::MockFence> mFenceUnsignaled2 = sp<mock::MockFence>::make();
-    sp<mock::MockFence> mFenceSignaled2 = sp<mock::MockFence>::make();
 
     struct TransactionInfo {
         Vector<ComposerState> states;
@@ -129,15 +118,6 @@
         transaction.frameTimelineInfo = frameTimelineInfo;
     }
 
-    void setupSingleWithComposer(TransactionInfo& transaction, uint32_t flags,
-                                 bool syncInputWindows, int64_t desiredPresentTime,
-                                 bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo,
-                                 const Vector<ComposerState>* states) {
-        setupSingle(transaction, flags, syncInputWindows, desiredPresentTime, isAutoTimestamp,
-                    frameTimelineInfo);
-        transaction.states = *states;
-    }
-
     void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
         ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
         EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
@@ -263,189 +243,6 @@
         EXPECT_EQ(0u, transactionQueue.size());
     }
 
-    void Flush_removesUnsignaledFromTheQueue(Vector<ComposerState> state1,
-                                             Vector<ComposerState> state2,
-                                             bool updateApplyToken = true) {
-        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-
-        TransactionInfo transactionA;
-        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
-                                /*syncInputWindows*/ false,
-                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
-                                FrameTimelineInfo{}, &state1);
-
-        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
-                                     transactionA.displays, transactionA.flags,
-                                     transactionA.applyToken, transactionA.inputWindowCommands,
-                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
-                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transactionA.id);
-
-        TransactionInfo transactionB;
-        if (updateApplyToken) {
-            transactionB.applyToken = sp<IBinder>();
-        }
-        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
-                                /*syncInputWindows*/ false,
-                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
-                                FrameTimelineInfo{}, &state2);
-        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
-                                     transactionB.displays, transactionB.flags,
-                                     transactionB.applyToken, transactionB.inputWindowCommands,
-                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
-                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transactionB.id);
-
-        mFlinger.flushTransactionQueues();
-        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
-        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
-        EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
-    }
-
-    void Flush_removesFromTheQueue(const Vector<ComposerState>& state) {
-        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
-
-        TransactionInfo transaction;
-        setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
-                                /*syncInputWindows*/ false,
-                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
-                                FrameTimelineInfo{}, &state);
-
-        mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
-                                     transaction.displays, transaction.flags,
-                                     transaction.applyToken, transaction.inputWindowCommands,
-                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transaction.id);
-
-        mFlinger.flushTransactionQueues();
-        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
-        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
-        EXPECT_EQ(1u, mFlinger.getTransactionCommittedSignals().size());
-    }
-
-    void Flush_keepsInTheQueue(const Vector<ComposerState>& state) {
-        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
-
-        TransactionInfo transaction;
-        setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
-                                /*syncInputWindows*/ false,
-                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
-                                FrameTimelineInfo{}, &state);
-
-        mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
-                                     transaction.displays, transaction.flags,
-                                     transaction.applyToken, transaction.inputWindowCommands,
-                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transaction.id);
-
-        mFlinger.flushTransactionQueues();
-        EXPECT_EQ(1u, mFlinger.getPendingTransactionQueue().size());
-        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
-        EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
-    }
-
-    void Flush_KeepsUnsignaledInTheQueue(const Vector<ComposerState>& state1,
-                                         const Vector<ComposerState>& state2,
-                                         bool updateApplyToken = true,
-                                         uint32_t pendingTransactionQueueSize = 1u) {
-        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
-        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-        auto time = systemTime();
-        TransactionInfo transactionA;
-        TransactionInfo transactionB;
-        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
-                                /*syncInputWindows*/ false,
-                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
-                                FrameTimelineInfo{}, &state1);
-        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
-                                /*syncInputWindows*/ false,
-                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
-                                FrameTimelineInfo{}, &state2);
-        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
-                                     transactionA.displays, transactionA.flags,
-                                     transactionA.applyToken, transactionA.inputWindowCommands,
-                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
-                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transactionA.id);
-        if (updateApplyToken) {
-            transactionB.applyToken = sp<IBinder>();
-        }
-        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
-                                     transactionB.displays, transactionB.flags,
-                                     transactionB.applyToken, transactionB.inputWindowCommands,
-                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
-                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transactionB.id);
-
-        mFlinger.flushTransactionQueues();
-        EXPECT_EQ(pendingTransactionQueueSize, mFlinger.getPendingTransactionQueue().size());
-        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
-    }
-
-    void Flush_removesSignaledFromTheQueue(const Vector<ComposerState>& state1,
-                                           const Vector<ComposerState>& state2) {
-        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
-        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
-
-        auto time = systemTime();
-        TransactionInfo transactionA;
-        TransactionInfo transactionB;
-        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
-                                /*syncInputWindows*/ false,
-                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
-                                FrameTimelineInfo{}, &state1);
-        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
-                                /*syncInputWindows*/ false,
-                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
-                                FrameTimelineInfo{}, &state2);
-        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
-                                     transactionA.displays, transactionA.flags,
-                                     transactionA.applyToken, transactionA.inputWindowCommands,
-                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
-                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transactionA.id);
-        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
-                                     transactionB.displays, transactionB.flags,
-                                     transactionB.applyToken, transactionB.inputWindowCommands,
-                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
-                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transactionB.id);
-
-        mFlinger.flushTransactionQueues();
-        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
-        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
-        EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
-    }
-
-    static Vector<ComposerState> createComposerStateVector(const ComposerState& state1,
-                                                           const ComposerState& state2) {
-        Vector<ComposerState> states;
-        states.push_back(state1);
-        states.push_back(state2);
-        return states;
-    }
-
-    static Vector<ComposerState> createComposerStateVector(const ComposerState& state) {
-        Vector<ComposerState> states;
-        states.push_back(state);
-        return states;
-    }
-
-    static ComposerState createComposerState(int layerId, sp<Fence> fence,
-                                             uint32_t stateFlags = layer_state_t::eBufferChanged) {
-        ComposerState composer_state;
-        composer_state.state.bufferData = std::make_shared<BufferData>();
-        composer_state.state.bufferData->acquireFence = std::move(fence);
-        composer_state.state.layerId = layerId;
-        composer_state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
-        composer_state.state.flags = stateFlags;
-        return composer_state;
-    }
-
     bool mHasListenerCallbacks = false;
     std::vector<ListenerCallbacks> mCallbacks;
     int mTransactionNumber = 0;
@@ -529,215 +326,721 @@
     EXPECT_EQ(nullptr, ret.promote().get());
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesSingleSignaledFromTheQueue_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_removesFromTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+class LatchUnsignaledTest : public TransactionApplicationTest {
+public:
+    void TearDown() override {
+        // Clear all transaction queues to release all transactions we sent
+        // in the tests. Otherwise, gmock complains about memory leaks.
+        mFlinger.getTransactionQueue().clear();
+        mFlinger.getPendingTransactionQueue().clear();
+        mFlinger.getTransactionCommittedSignals().clear();
+        mFlinger.commitTransactionsLocked(eTransactionMask);
+        mFlinger.mutableCurrentState().layersSortedByZ.clear();
+        mFlinger.mutableDrawingState().layersSortedByZ.clear();
+    }
+
+    static sp<Fence> fence(Fence::Status status) {
+        const auto fence = sp<mock::MockFence>::make();
+        EXPECT_CALL(*fence, getStatus()).WillRepeatedly(Return(status));
+        return fence;
+    }
+
+    ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) {
+        ComposerState state;
+        state.state.bufferData = std::make_shared<BufferData>();
+        state.state.bufferData->acquireFence = std::move(fence);
+        state.state.layerId = layerId;
+        state.state.surface =
+                sp<BufferStateLayer>::make(
+                        LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
+                        ->getHandle();
+        state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
+
+        state.state.what = what;
+        if (what & layer_state_t::eCropChanged) {
+            state.state.crop = Rect(1, 2, 3, 4);
+        }
+        return state;
+    }
+
+    TransactionInfo createTransactionInfo(const sp<IBinder>& applyToken,
+                                          const std::vector<ComposerState>& states) {
+        TransactionInfo transaction;
+        const uint32_t kFlags = ISurfaceComposer::eSynchronous;
+        const bool kSyncInputWindows = false;
+        const nsecs_t kDesiredPresentTime = systemTime();
+        const bool kIsAutoTimestamp = true;
+        const auto kFrameTimelineInfo = FrameTimelineInfo{};
+
+        setupSingle(transaction, kFlags, kSyncInputWindows, kDesiredPresentTime, kIsAutoTimestamp,
+                    kFrameTimelineInfo);
+        transaction.applyToken = applyToken;
+        for (const auto& state : states) {
+            transaction.states.push_back(state);
+        }
+
+        return transaction;
+    }
+
+    void setTransactionStates(const std::vector<TransactionInfo>& transactions,
+                              size_t expectedTransactionsApplied,
+                              size_t expectedTransactionsPending) {
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+        for (const auto& transaction : transactions) {
+            mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+                                         transaction.displays, transaction.flags,
+                                         transaction.applyToken, transaction.inputWindowCommands,
+                                         transaction.desiredPresentTime,
+                                         transaction.isAutoTimestamp, transaction.uncacheBuffer,
+                                         mHasListenerCallbacks, mCallbacks, transaction.id);
+        }
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(expectedTransactionsApplied, mFlinger.getTransactionCommittedSignals().size());
+    }
+};
+
+class LatchUnsignaledAutoSingleLayerTest : public LatchUnsignaledTest {
+public:
+    void SetUp() override {
+        LatchUnsignaledTest::SetUp();
+        SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::AutoSingleLayer;
+    }
+};
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleSignaledFromTheQueue) {
+    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);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesSingleUnSignaledFromTheQueue_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_removesFromTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+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 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest,
-       Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_keepsInTheQueue(createComposerStateVector(
-            createComposerState(/*layerId*/ 1, mFenceUnsignaled, layer_state_t::eCropChanged)));
+TEST_F(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(TransactionApplicationTest,
-       Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_keepsInTheQueue(createComposerStateVector(
-            createComposerState(/*layerId*/ 1, mFenceUnsignaled,
-                                layer_state_t::eCropChanged | layer_state_t::eBufferChanged)));
+TEST_F(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 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eCropChanged |
+                                                                      layer_state_t::
+                                                                              eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest,
-       Flush_KeepsInTheQueueSameApplyTokenMultiState_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_keepsInTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
-                                      createComposerState(/*layerId*/ 1, mFenceSignaled)));
+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 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_MultipleStateTransaction_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_keepsInTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
-                                      createComposerState(/*layerId*/ 2, mFenceSignaled)));
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueue_MultipleStateTransaction) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 0u;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto mixedTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_removesSignaledFromTheQueue(createComposerStateVector(
-                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
-                                      createComposerStateVector(
-                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSignaledFromTheQueue) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 2u;
+    const auto kExpectedTransactionsPending = 0u;
+
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto signaledTransaction2 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemoveSignaledWithUnsignaledIntact_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
-                                            createComposerState(/*layerId*/ 1, mFenceSignaled)),
-                                    createComposerStateVector(
-                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
-    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemoveSignaledWithUnsignaledIntact) {
+    const sp<IBinder> kApplyToken1 =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    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 signaledTransaction =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken2,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest,
-       Flush_KeepsTransactionInTheQueueSameApplyToken_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
-                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
-                                    createComposerStateVector(
-                                            createComposerState(/*layerId*/ 2, mFenceSignaled)),
-                                    /*updateApplyToken*/ false);
-    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 1u;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_KeepsTransactionInTheQueue_LatchUnsignaled_Auto) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
-    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
-                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
-                                    createComposerStateVector(
-                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
-                                    /*updateApplyToken*/ true,
-                                    /*pendingTransactionQueueSize*/ 2u);
-    EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueue) {
+    const sp<IBinder> kApplyToken1 =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    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 =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto unsignaledTransaction2 =
+            createTransactionInfo(kApplyToken2,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
+                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Disabled) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
-    Flush_removesFromTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+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 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+
+    // Get VsyncModulator out of the default config
+    static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
+
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_LatchUnsignaled_Disabled) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
-    Flush_keepsInTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+class LatchUnsignaledDisabledTest : public LatchUnsignaledTest {
+public:
+    void SetUp() override {
+        LatchUnsignaledTest::SetUp();
+        SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    }
+};
+
+TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue) {
+    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);
 }
 
-TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueSameLayerId_LatchUnsignaled_Disabled) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
-    Flush_keepsInTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
-                                      createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+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 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueDifferentLayerId_LatchUnsignaled_Disabled) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
-    Flush_keepsInTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
-                                      createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+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 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Disabled) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
-    Flush_removesSignaledFromTheQueue(createComposerStateVector(
-                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
-                                      createComposerStateVector(
-                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueDifferentLayerId) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 0u;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest,
-       Flush_KeepInTheQueueDifferentApplyToken_LatchUnsignaled_Disabled) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
-    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
-                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
-                                    createComposerStateVector(
-                                            createComposerState(/*layerId*/ 2, mFenceSignaled)));
-    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 2u;
+    const auto kExpectedTransactionsPending = 0u;
+
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto signaledTransaction2 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_KeepInTheQueueSameApplyToken_LatchUnsignaled_Disabled) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
-    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
-                                            createComposerState(/*layerId*/ 1, mFenceSignaled)),
-                                    createComposerStateVector(
-                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
-                                    /*updateApplyToken*/ false);
-    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueDifferentApplyToken) {
+    const sp<IBinder> kApplyToken1 =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    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 =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken2,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_KeepInTheUnsignaledTheQueue_LatchUnsignaled_Disabled) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
-    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
-                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
-                                    createComposerStateVector(
-                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
-                                    /*updateApplyToken*/ false);
-    EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueSameApplyToken) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 1u;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Always) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    Flush_removesFromTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheUnsignaledTheQueue) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 0u;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto unsignaledTransaction2 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
+                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueue_LatchUnsignaled_Always) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    Flush_removesFromTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest {
+public:
+    void SetUp() override {
+        LatchUnsignaledTest::SetUp();
+        SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    }
+};
+
+TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue) {
+    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);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueueSameLayerId_LatchUnsignaled_Always) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    Flush_removesFromTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
-                                      createComposerState(/*layerId*/ 1, mFenceSignaled)));
+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);
 }
 
-TEST_F(TransactionApplicationTest,
-       Flush_RemovesFromTheQueueDifferentLayerId_LatchUnsignaled_Always) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    Flush_removesFromTheQueue(
-            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
-                                      createComposerState(/*layerId*/ 2, mFenceSignaled)));
+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 =
+            createTransactionInfo(kApplyToken,
+                                  {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
+                                                       layer_state_t::eBufferChanged),
+                                   createComposerState(kLayerId, fence(Fence::Status::Signaled),
+                                                       layer_state_t::eBufferChanged)});
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Always) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    Flush_removesSignaledFromTheQueue(createComposerStateVector(
-                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
-                                      createComposerStateVector(
-                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 1u;
+    const auto kExpectedTransactionsPending = 0u;
+
+    const auto mixedTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled),
+                                                       layer_state_t::eBufferChanged),
+                                   createComposerState(kLayerId2, fence(Fence::Status::Signaled),
+                                                       layer_state_t::eBufferChanged)});
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest,
-       Flush_RemovesFromTheQueueDifferentApplyToken_LatchUnsignaled_Always) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
-                                                createComposerState(/*layerId*/ 1, mFenceSignaled)),
-                                        createComposerStateVector(
-                                                createComposerState(/*layerId*/ 2,
-                                                                    mFenceUnsignaled)));
+TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 2u;
+    const auto kExpectedTransactionsPending = 0u;
+
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto signaledTransaction2 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest,
-       Flush_RemovesUnsignaledFromTheQueueSameApplyToken_LatchUnsignaled_Always) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
-                                                createComposerState(/*layerId*/ 1,
-                                                                    mFenceUnsignaled)),
-                                        createComposerStateVector(
-                                                createComposerState(/*layerId*/ 2, mFenceSignaled)),
-                                        /*updateApplyToken*/ false);
+TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) {
+    const sp<IBinder> kApplyToken1 =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    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 =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken2,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
-TEST_F(TransactionApplicationTest, Flush_RemovesUnsignaledFromTheQueue_LatchUnsignaled_Always) {
-    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
-    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
-                                                createComposerState(/*layerId*/ 1,
-                                                                    mFenceUnsignaled)),
-                                        createComposerStateVector(
-                                                createComposerState(/*layerId*/ 2,
-                                                                    mFenceUnsignaled)));
+TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueueSameApplyToken) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsApplied = 2u;
+    const auto kExpectedTransactionsPending = 0u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesUnsignaledFromTheQueue) {
+    const sp<IBinder> kApplyToken1 =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    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 =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto unsignaledTransaction2 =
+            createTransactionInfo(kApplyToken2,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
+                         kExpectedTransactionsApplied, 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 =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+
+    // Get VsyncModulator out of the default config
+    static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
+
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
+                         kExpectedTransactionsPending);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index 271b1c0..ab893a3 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -70,23 +70,32 @@
         t1.displays.add(display);
     }
 
-    TransactionProtoParser::LayerHandleToIdFn getLayerIdFn = [&](const sp<IBinder>& handle) {
-        return (handle == layerHandle) ? 42 : -1;
-    };
-    TransactionProtoParser::DisplayHandleToIdFn getDisplayIdFn = [&](const sp<IBinder>& handle) {
-        return (handle == displayHandle) ? 43 : -1;
-    };
-    TransactionProtoParser::LayerIdToHandleFn getLayerHandleFn = [&](int32_t id) {
-        return (id == 42) ? layerHandle : nullptr;
-    };
-    TransactionProtoParser::DisplayIdToHandleFn getDisplayHandleFn = [&](int32_t id) {
-        return (id == 43) ? displayHandle : nullptr;
+    class TestMapper : public TransactionProtoParser::FlingerDataMapper {
+    public:
+        sp<IBinder> layerHandle;
+        sp<IBinder> displayHandle;
+
+        TestMapper(sp<IBinder> layerHandle, sp<IBinder> displayHandle)
+              : layerHandle(layerHandle), displayHandle(displayHandle) {}
+
+        sp<IBinder> getLayerHandle(int32_t id) const override {
+            return (id == 42) ? layerHandle : nullptr;
+        }
+        int32_t getLayerId(const sp<IBinder>& handle) const override {
+            return (handle == layerHandle) ? 42 : -1;
+        }
+        sp<IBinder> getDisplayHandle(int32_t id) const {
+            return (id == 43) ? displayHandle : nullptr;
+        }
+        int32_t getDisplayId(const sp<IBinder>& handle) const {
+            return (handle == displayHandle) ? 43 : -1;
+        }
     };
 
-    proto::TransactionState proto =
-            TransactionProtoParser::toProto(t1, getLayerIdFn, getDisplayIdFn);
-    TransactionState t2 =
-            TransactionProtoParser::fromProto(proto, getLayerHandleFn, getDisplayHandleFn);
+    TransactionProtoParser parser(std::make_unique<TestMapper>(layerHandle, displayHandle));
+
+    proto::TransactionState proto = parser.toProto(t1);
+    TransactionState t2 = parser.fromProto(proto);
 
     ASSERT_EQ(t1.originPid, t2.originPid);
     ASSERT_EQ(t1.originUid, t2.originUid);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 39dbb07..61b72a0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -178,8 +178,8 @@
     // verify we can still retrieve the layer change from the first entry containing starting
     // states.
     EXPECT_GT(proto.entry().size(), 0);
-    EXPECT_GT(proto.entry(0).transactions().size(), 0);
-    EXPECT_GT(proto.entry(0).added_layers().size(), 0);
+    EXPECT_EQ(proto.entry(0).transactions().size(), 1);
+    EXPECT_EQ(proto.entry(0).added_layers().size(), 2);
     EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
     EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
     EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 99d94f7..a1aa7e8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -43,6 +43,8 @@
 using android::hardware::graphics::composer::V2_4::IComposerCallback;
 using android::hardware::graphics::composer::V2_4::IComposerClient;
 
+using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
+
 class Composer : public Hwc2::Composer {
 public:
     using Display = android::hardware::graphics::composer::V2_1::Display;
@@ -153,6 +155,8 @@
     MOCK_METHOD3(setLayerBrightness, Error(Display, Layer, float));
     MOCK_METHOD3(setLayerBlockingRegion,
                  Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
+    MOCK_METHOD2(getDisplayDecorationSupport,
+                 Error(Display, std::optional<DisplayDecorationSupport>*));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 85c65cf..570ffeb 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -95,6 +95,10 @@
     MOCK_METHOD(hal::Error, setContentType, (hal::ContentType), (override));
     MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *, float *),
                 (override));
+    MOCK_METHOD(
+            hal::Error, getDisplayDecorationSupport,
+            (std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport> *),
+            (override));
 };
 
 class Layer : public HWC2::Layer {