Merge "Revert^2 "Group surfaceflinger mock files for use in fuzzers""
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 8da1352..1c3a4f2 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -11,286 +11,6 @@
 
 # Grant unix world read/write permissions to kernel tracepoints.
 # Access control to these files is now entirely in selinux policy.
-    chmod 0755 /sys/kernel/debug/tracing/events
-    chmod 0755 /sys/kernel/debug/tracing/events/binder
-    chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_lock
-    chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_locked
-    chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_set_priority
-    chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_transaction
-    chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_transaction_alloc_buf
-    chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_transaction_received
-    chmod 0755 /sys/kernel/debug/tracing/events/binder/binder_unlock
-    chmod 0755 /sys/kernel/debug/tracing/events/block
-    chmod 0755 /sys/kernel/debug/tracing/events/block/block_rq_complete
-    chmod 0755 /sys/kernel/debug/tracing/events/block/block_rq_issue
-    chmod 0755 /sys/kernel/debug/tracing/events/cgroup
-    chmod 0755 /sys/kernel/debug/tracing/events/clk
-    chmod 0755 /sys/kernel/debug/tracing/events/clk/clk_disable
-    chmod 0755 /sys/kernel/debug/tracing/events/clk/clk_enable
-    chmod 0755 /sys/kernel/debug/tracing/events/clk/clk_set_rate
-    chmod 0755 /sys/kernel/debug/tracing/events/cpufreq_interactive
-    chmod 0755 /sys/kernel/debug/tracing/events/cpuhp
-    chmod 0755 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_enter
-    chmod 0755 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_pause
-    chmod 0755 /sys/kernel/debug/tracing/events/dma_fence
-    chmod 0755 /sys/kernel/debug/tracing/events/ext4
-    chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin
-    chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end
-    chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_es_lookup_extent_enter
-    chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_es_lookup_extent_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_load_inode
-    chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter
-    chmod 0755 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/f2fs
-    chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_get_data_block
-    chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_iget
-    chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter
-    chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin
-    chmod 0755 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end
-    chmod 0755 /sys/kernel/debug/tracing/events/fence
-    chmod 0755 /sys/kernel/debug/tracing/events/filemap
-    chmod 0755 /sys/kernel/debug/tracing/events/filemap/mm_filemap_add_to_page_cache
-    chmod 0755 /sys/kernel/debug/tracing/events/filemap/mm_filemap_delete_from_page_cache
-    chmod 0755 /sys/kernel/debug/tracing/events/gpu_mem
-    chmod 0755 /sys/kernel/debug/tracing/events/gpu_mem/gpu_mem_total
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c/i2c_read
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c/i2c_reply
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c/i2c_result
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c/i2c_write
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c/smbus_read
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c/smbus_reply
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c/smbus_result
-    chmod 0755 /sys/kernel/debug/tracing/events/i2c/smbus_write
-    chmod 0755 /sys/kernel/debug/tracing/events/ion
-    chmod 0755 /sys/kernel/debug/tracing/events/ion/ion_stat
-    chmod 0755 /sys/kernel/debug/tracing/events/ipi
-    chmod 0755 /sys/kernel/debug/tracing/events/ipi/ipi_entry
-    chmod 0755 /sys/kernel/debug/tracing/events/ipi/ipi_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/ipi/ipi_raise
-    chmod 0755 /sys/kernel/debug/tracing/events/irq
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/irq_handler_entry
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/irq_handler_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/softirq_entry
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/softirq_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/softirq_raise
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/tasklet_entry
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/tasklet_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/tasklet_hi_entry
-    chmod 0755 /sys/kernel/debug/tracing/events/irq/tasklet_hi_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/kmem
-    chmod 0755 /sys/kernel/debug/tracing/events/kmem/ion_heap_grow
-    chmod 0755 /sys/kernel/debug/tracing/events/kmem/ion_heap_shrink
-    chmod 0755 /sys/kernel/debug/tracing/events/kmem/rss_stat
-    chmod 0755 /sys/kernel/debug/tracing/events/lowmemorykiller
-    chmod 0755 /sys/kernel/debug/tracing/events/lowmemorykiller/lowmemory_kill
-    chmod 0755 /sys/kernel/debug/tracing/events/mm_event
-    chmod 0755 /sys/kernel/debug/tracing/events/mm_event/mm_event_record
-    chmod 0755 /sys/kernel/debug/tracing/events/oom
-    chmod 0755 /sys/kernel/debug/tracing/events/oom/mark_victim
-    chmod 0755 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update
-    chmod 0755 /sys/kernel/debug/tracing/events/power
-    chmod 0755 /sys/kernel/debug/tracing/events/power/clock_disable
-    chmod 0755 /sys/kernel/debug/tracing/events/power/clock_enable
-    chmod 0755 /sys/kernel/debug/tracing/events/power/clock_set_rate
-    chmod 0755 /sys/kernel/debug/tracing/events/power/cpu_frequency
-    chmod 0755 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits
-    chmod 0755 /sys/kernel/debug/tracing/events/power/cpu_idle
-    chmod 0755 /sys/kernel/debug/tracing/events/power/gpu_frequency
-    chmod 0755 /sys/kernel/debug/tracing/events/power/suspend_resume
-    chmod 0755 /sys/kernel/debug/tracing/events/sched
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_pi_setprio
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_process_exit
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_process_free
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_switch
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_wakeup
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_wakeup_new
-    chmod 0755 /sys/kernel/debug/tracing/events/sched/sched_waking
-    chmod 0755 /sys/kernel/debug/tracing/events/signal
-    chmod 0755 /sys/kernel/debug/tracing/events/signal/signal_deliver
-    chmod 0755 /sys/kernel/debug/tracing/events/signal/signal_generate
-    chmod 0755 /sys/kernel/debug/tracing/events/sync
-    chmod 0755 /sys/kernel/debug/tracing/events/task
-    chmod 0755 /sys/kernel/debug/tracing/events/task/task_newtask
-    chmod 0755 /sys/kernel/debug/tracing/events/task/task_rename
-    chmod 0755 /sys/kernel/debug/tracing/events/thermal
-    chmod 0755 /sys/kernel/debug/tracing/events/thermal/cdev_update
-    chmod 0755 /sys/kernel/debug/tracing/events/thermal/thermal_temperature
-    chmod 0755 /sys/kernel/debug/tracing/events/vmscan
-    chmod 0755 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin
-    chmod 0755 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end
-    chmod 0755 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep
-    chmod 0755 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake
-    chmod 0755 /sys/kernel/debug/tracing/options
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu0
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu1
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu2
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu3
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu4
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu5
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu6
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu7
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu8
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu9
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu10
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu11
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu12
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu13
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu14
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu15
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu16
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu17
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu18
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu19
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu20
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu21
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu22
-    chmod 0755 /sys/kernel/debug/tracing/per_cpu/cpu23
-    chmod 0755 /sys/kernel/tracing/events
-    chmod 0755 /sys/kernel/tracing/events/binder
-    chmod 0755 /sys/kernel/tracing/events/binder/binder_lock
-    chmod 0755 /sys/kernel/tracing/events/binder/binder_locked
-    chmod 0755 /sys/kernel/tracing/events/binder/binder_set_priority
-    chmod 0755 /sys/kernel/tracing/events/binder/binder_transaction
-    chmod 0755 /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf
-    chmod 0755 /sys/kernel/tracing/events/binder/binder_transaction_received
-    chmod 0755 /sys/kernel/tracing/events/binder/binder_unlock
-    chmod 0755 /sys/kernel/tracing/events/block
-    chmod 0755 /sys/kernel/tracing/events/block/block_rq_complete
-    chmod 0755 /sys/kernel/tracing/events/block/block_rq_issue
-    chmod 0755 /sys/kernel/tracing/events/cgroup
-    chmod 0755 /sys/kernel/tracing/events/clk
-    chmod 0755 /sys/kernel/tracing/events/clk/clk_disable
-    chmod 0755 /sys/kernel/tracing/events/clk/clk_enable
-    chmod 0755 /sys/kernel/tracing/events/clk/clk_set_rate
-    chmod 0755 /sys/kernel/tracing/events/cpufreq_interactive
-    chmod 0755 /sys/kernel/tracing/events/cpuhp
-    chmod 0755 /sys/kernel/tracing/events/cpuhp/cpuhp_enter
-    chmod 0755 /sys/kernel/tracing/events/cpuhp/cpuhp_exit
-    chmod 0755 /sys/kernel/tracing/events/cpuhp/cpuhp_pause
-    chmod 0755 /sys/kernel/tracing/events/dma_fence
-    chmod 0755 /sys/kernel/tracing/events/ext4
-    chmod 0755 /sys/kernel/tracing/events/ext4/ext4_da_write_begin
-    chmod 0755 /sys/kernel/tracing/events/ext4/ext4_da_write_end
-    chmod 0755 /sys/kernel/tracing/events/ext4/ext4_es_lookup_extent_enter
-    chmod 0755 /sys/kernel/tracing/events/ext4/ext4_es_lookup_extent_exit
-    chmod 0755 /sys/kernel/tracing/events/ext4/ext4_load_inode
-    chmod 0755 /sys/kernel/tracing/events/ext4/ext4_sync_file_enter
-    chmod 0755 /sys/kernel/tracing/events/ext4/ext4_sync_file_exit
-    chmod 0755 /sys/kernel/tracing/events/f2fs
-    chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block
-    chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_iget
-    chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_enter
-    chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_exit
-    chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_write_begin
-    chmod 0755 /sys/kernel/tracing/events/f2fs/f2fs_write_end
-    chmod 0755 /sys/kernel/tracing/events/fence
-    chmod 0755 /sys/kernel/tracing/events/filemap
-    chmod 0755 /sys/kernel/tracing/events/filemap/mm_filemap_add_to_page_cache
-    chmod 0755 /sys/kernel/tracing/events/filemap/mm_filemap_delete_from_page_cache
-    chmod 0755 /sys/kernel/tracing/events/gpu_mem
-    chmod 0755 /sys/kernel/tracing/events/gpu_mem/gpu_mem_total
-    chmod 0755 /sys/kernel/tracing/events/i2c
-    chmod 0755 /sys/kernel/tracing/events/i2c/i2c_read
-    chmod 0755 /sys/kernel/tracing/events/i2c/i2c_reply
-    chmod 0755 /sys/kernel/tracing/events/i2c/i2c_result
-    chmod 0755 /sys/kernel/tracing/events/i2c/i2c_write
-    chmod 0755 /sys/kernel/tracing/events/i2c/smbus_read
-    chmod 0755 /sys/kernel/tracing/events/i2c/smbus_reply
-    chmod 0755 /sys/kernel/tracing/events/i2c/smbus_result
-    chmod 0755 /sys/kernel/tracing/events/i2c/smbus_write
-    chmod 0755 /sys/kernel/tracing/events/ion
-    chmod 0755 /sys/kernel/tracing/events/ion/ion_stat
-    chmod 0755 /sys/kernel/tracing/events/ipi
-    chmod 0755 /sys/kernel/tracing/events/ipi/ipi_entry
-    chmod 0755 /sys/kernel/tracing/events/ipi/ipi_exit
-    chmod 0755 /sys/kernel/tracing/events/ipi/ipi_raise
-    chmod 0755 /sys/kernel/tracing/events/irq
-    chmod 0755 /sys/kernel/tracing/events/irq/irq_handler_entry
-    chmod 0755 /sys/kernel/tracing/events/irq/irq_handler_exit
-    chmod 0755 /sys/kernel/tracing/events/irq/softirq_entry
-    chmod 0755 /sys/kernel/tracing/events/irq/softirq_exit
-    chmod 0755 /sys/kernel/tracing/events/irq/softirq_raise
-    chmod 0755 /sys/kernel/tracing/events/irq/tasklet_entry
-    chmod 0755 /sys/kernel/tracing/events/irq/tasklet_exit
-    chmod 0755 /sys/kernel/tracing/events/irq/tasklet_hi_entry
-    chmod 0755 /sys/kernel/tracing/events/irq/tasklet_hi_exit
-    chmod 0755 /sys/kernel/tracing/events/kmem
-    chmod 0755 /sys/kernel/tracing/events/kmem/ion_heap_grow
-    chmod 0755 /sys/kernel/tracing/events/kmem/ion_heap_shrink
-    chmod 0755 /sys/kernel/tracing/events/kmem/rss_stat
-    chmod 0755 /sys/kernel/tracing/events/lowmemorykiller
-    chmod 0755 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill
-    chmod 0755 /sys/kernel/tracing/events/mm_event
-    chmod 0755 /sys/kernel/tracing/events/mm_event/mm_event_record
-    chmod 0755 /sys/kernel/tracing/events/oom
-    chmod 0755 /sys/kernel/tracing/events/oom/mark_victim
-    chmod 0755 /sys/kernel/tracing/events/oom/oom_score_adj_update
-    chmod 0755 /sys/kernel/tracing/events/power
-    chmod 0755 /sys/kernel/tracing/events/power/clock_disable
-    chmod 0755 /sys/kernel/tracing/events/power/clock_enable
-    chmod 0755 /sys/kernel/tracing/events/power/clock_set_rate
-    chmod 0755 /sys/kernel/tracing/events/power/cpu_frequency
-    chmod 0755 /sys/kernel/tracing/events/power/cpu_frequency_limits
-    chmod 0755 /sys/kernel/tracing/events/power/cpu_idle
-    chmod 0755 /sys/kernel/tracing/events/power/gpu_frequency
-    chmod 0755 /sys/kernel/tracing/events/power/suspend_resume
-    chmod 0755 /sys/kernel/tracing/events/sched
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_blocked_reason
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_cpu_hotplug
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_pi_setprio
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_process_exit
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_process_free
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_switch
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_wakeup
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_wakeup_new
-    chmod 0755 /sys/kernel/tracing/events/sched/sched_waking
-    chmod 0755 /sys/kernel/tracing/events/signal
-    chmod 0755 /sys/kernel/tracing/events/signal/signal_deliver
-    chmod 0755 /sys/kernel/tracing/events/signal/signal_generate
-    chmod 0755 /sys/kernel/tracing/events/sync
-    chmod 0755 /sys/kernel/tracing/events/task
-    chmod 0755 /sys/kernel/tracing/events/task/task_newtask
-    chmod 0755 /sys/kernel/tracing/events/task/task_rename
-    chmod 0755 /sys/kernel/tracing/events/thermal
-    chmod 0755 /sys/kernel/tracing/events/thermal/cdev_update
-    chmod 0755 /sys/kernel/tracing/events/thermal/thermal_temperature
-    chmod 0755 /sys/kernel/tracing/events/vmscan
-    chmod 0755 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin
-    chmod 0755 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end
-    chmod 0755 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep
-    chmod 0755 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake
-    chmod 0755 /sys/kernel/tracing/options
-    chmod 0755 /sys/kernel/tracing/per_cpu
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu0
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu1
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu2
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu3
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu4
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu5
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu6
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu7
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu8
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu9
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu10
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu11
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu12
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu13
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu14
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu15
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu16
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu17
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu18
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu19
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu20
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu21
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu22
-    chmod 0755 /sys/kernel/tracing/per_cpu/cpu23
     chmod 0666 /sys/kernel/debug/tracing/trace_clock
     chmod 0666 /sys/kernel/tracing/trace_clock
     chmod 0666 /sys/kernel/debug/tracing/buffer_size_kb
@@ -315,6 +35,8 @@
     chmod 0666 /sys/kernel/tracing/events/sched/sched_pi_setprio/enable
     chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable
     chmod 0666 /sys/kernel/tracing/events/sched/sched_process_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_free/enable
+    chmod 0666 /sys/kernel/tracing/events/sched/sched_process_free/enable
     chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_waking/enable
     chmod 0666 /sys/kernel/tracing/events/sched/sched_waking/enable
     chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 32e680d..e97949e 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1802,8 +1802,8 @@
     // Add linker configuration directory
     ds.AddDir(LINKERCONFIG_DIR, true);
 
-    /* Dump cgroupfs */
-    ds.AddDir(CGROUPFS_DIR, true);
+    /* Dump frozen cgroupfs */
+    dump_frozen_cgroupfs();
 
     if (ds.dump_pool_) {
         WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
@@ -4169,6 +4169,63 @@
     fclose(fp);
 }
 
+void dump_frozen_cgroupfs(const char *dir, int level,
+        int (*dump_from_fd)(const char* title, const char* path, int fd)) {
+    DIR *dirp;
+    struct dirent *d;
+    char *newpath = nullptr;
+
+    dirp = opendir(dir);
+    if (dirp == nullptr) {
+        MYLOGE("%s: %s\n", dir, strerror(errno));
+        return;
+    }
+
+    for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) {
+        if ((d->d_name[0] == '.')
+         && (((d->d_name[1] == '.') && (d->d_name[2] == '\0'))
+          || (d->d_name[1] == '\0'))) {
+            continue;
+        }
+        if (d->d_type == DT_DIR) {
+            asprintf(&newpath, "%s/%s/", dir, d->d_name);
+            if (!newpath) {
+                continue;
+            }
+            if (level == 0 && !strncmp(d->d_name, "uid_", 4)) {
+                dump_frozen_cgroupfs(newpath, 1, dump_from_fd);
+            } else if (level == 1 && !strncmp(d->d_name, "pid_", 4)) {
+                char *freezer = nullptr;
+                asprintf(&freezer, "%s/%s", newpath, "cgroup.freeze");
+                if (freezer) {
+                    FILE* fp = fopen(freezer, "r");
+                    if (fp != NULL) {
+                        int frozen;
+                        fscanf(fp, "%d", &frozen);
+                        if (frozen > 0) {
+                            dump_files("", newpath, skip_none, dump_from_fd);
+                        }
+                        fclose(fp);
+                    }
+                    free(freezer);
+                }
+            }
+        }
+    }
+    closedir(dirp);
+}
+
+void dump_frozen_cgroupfs() {
+    if (!ds.IsZipping()) {
+        MYLOGD("Not adding cgroupfs because it's not a zipped bugreport\n");
+        return;
+    }
+    MYLOGD("Adding frozen processes from %s\n", CGROUPFS_DIR);
+    DurationReporter duration_reporter("FROZEN CGROUPFS");
+    if (PropertiesHelper::IsDryRun()) return;
+    dump_frozen_cgroupfs(CGROUPFS_DIR, 0, _add_file_from_fd);
+}
+
 void Dumpstate::UpdateProgress(int32_t delta_sec) {
     if (progress_ == nullptr) {
         MYLOGE("UpdateProgress: progress_ not set\n");
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 773e292..852b9a8 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -637,6 +637,9 @@
 /* Prints the contents of all the routing tables, both IPv4 and IPv6. */
 void dump_route_tables();
 
+/* Dump subdirectories of cgroupfs if the corresponding process is frozen */
+void dump_frozen_cgroupfs();
+
 /* Play a sound via Stagefright */
 void play_sound(const char *path);
 
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 289c2ae..2207405 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -1131,16 +1131,15 @@
 }
 
 static int32_t copy_directory_recursive(const char* from, const char* to) {
-    char *argv[] = {
-        (char*) kCpPath,
-        (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-        (char*) "-p", /* preserve timestamps, ownership, and permissions */
-        (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-        (char*) "-P", /* Do not follow symlinks [default] */
-        (char*) "-d", /* don't dereference symlinks */
-        (char*) from,
-        (char*) to
-    };
+    char* argv[] =
+            {(char*)kCpPath,
+             (char*)"-F", /* delete any existing destination file first (--remove-destination) */
+             (char*)"--preserve=mode,ownership,timestamps,xattr", /* preserve properties */
+             (char*)"-R", /* recurse into subdirectories (DEST must be a directory) */
+             (char*)"-P", /* Do not follow symlinks [default] */
+             (char*)"-d", /* don't dereference symlinks */
+             (char*)from,
+             (char*)to};
 
     LOG(DEBUG) << "Copying " << from << " to " << to;
     return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 80c0548..32922ca 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -47,6 +47,14 @@
 }
 
 cc_binary {
+    name: "servicemanager.microdroid",
+    defaults: ["servicemanager_defaults"],
+    init_rc: ["servicemanager.microdroid.rc"],
+    srcs: ["main.cpp"],
+    bootstrap: true,
+}
+
+cc_binary {
     name: "servicemanager.recovery",
     stem: "servicemanager",
     recovery: true,
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 4374abe..555be1ed7 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -113,8 +113,8 @@
     if (!found) {
         // Although it is tested, explicitly rebuilding qualified name, in case it
         // becomes something unexpected.
-        LOG(ERROR) << "Could not find " << aname.package << "." << aname.iface << "/"
-                   << aname.instance << " in the VINTF manifest.";
+        LOG(INFO) << "Could not find " << aname.package << "." << aname.iface << "/"
+                  << aname.instance << " in the VINTF manifest.";
     }
 
     return found;
diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc
new file mode 100644
index 0000000..e01f132
--- /dev/null
+++ b/cmds/servicemanager/servicemanager.microdroid.rc
@@ -0,0 +1,8 @@
+service servicemanager /system/bin/servicemanager.microdroid
+    class core
+    user system
+    group system readproc
+    critical
+    onrestart restart apexd
+    task_profiles ServiceCapacityLow
+    shutdown critical
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 931c5e3..31dee23 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -191,6 +191,12 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.wifi.direct.prebuilt.xml",
+    src: "android.hardware.wifi.direct.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.wifi.passpoint.prebuilt.xml",
     src: "android.hardware.wifi.passpoint.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
@@ -239,6 +245,12 @@
 }
 
 prebuilt_etc {
+    name: "go_handheld_core_hardware.prebuilt.xml",
+    src: "go_handheld_core_hardware.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "handheld_core_hardware.prebuilt.xml",
     src: "handheld_core_hardware.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.vulkan.version-1_3.xml b/data/etc/android.hardware.vulkan.version-1_3.xml
new file mode 100644
index 0000000..4ecea7b
--- /dev/null
+++ b/data/etc/android.hardware.vulkan.version-1_3.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device has a Vulkan
+     driver that supports API version 1.3 (0x00403000) -->
+<permissions>
+    <feature name="android.hardware.vulkan.version" version="4206592" />
+</permissions>
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 0389e57..98f0eec 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -39,6 +39,12 @@
  */
 typedef struct AChoreographer AChoreographer;
 
+
+/**
+ * The identifier of a frame timeline.
+ */
+typedef int64_t AVsyncId;
+
 struct AChoreographerFrameCallbackData;
 /**
  * Opaque type that provides access to an AChoreographerFrameCallbackData object.
@@ -203,19 +209,19 @@
 /**
  * The vsync ID token used to map Choreographer data.
  */
-int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
 
 /**
  * The time in nanoseconds which the frame at given index is expected to be presented.
  */
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
 
 /**
  * The time in nanoseconds which the frame at given index needs to be ready by.
  */
-int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
 
 __END_DECLS
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 509ee0e..4c83a14 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -216,6 +216,61 @@
  */
 void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29);
 
+/*
+ * Set the socket tag and owning UID for traffic statistics on the specified
+ * socket.
+ *
+ * Subsequent calls always replace any existing parameters. The socket tag and
+ * uid (if set) are kept when the socket is sent to another process using binder
+ * IPCs or other mechanisms such as UNIX socket fd passing. Any app can accept
+ * blame for future traffic performed on a socket originally created by another
+ * app by calling this method with its own UID (or calling
+ * android_tag_socket(int sockfd, int tag)). However, only apps holding the
+ * android.Manifest.permission#UPDATE_DEVICE_STATS permission may assign blame
+ * to another UIDs. If unset (default) the socket tag is 0, and the uid is the
+ * socket creator's uid.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN(33);
+
+/*
+ * Set the socket tag for traffic statistics on the specified socket.
+ *
+ * This function tags the socket with the caller's UID (accepting blame for
+ * future traffic performed on this socket) even if the socket was originally
+ * opened by another UID or was previously tagged by another UID. Subsequent
+ * calls always replace any existing parameters. The socket tag is kept when the
+ * socket is sent to another process using binder IPCs or other mechanisms such
+ * as UNIX socket fd passing.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33);
+
+/*
+ * Untag a network socket.
+ *
+ * Future traffic on this socket will no longer be associated with any
+ * previously configured tag and uid. If the socket was created by another UID
+ * or was previously tagged by another UID, calling this function will clear the
+ * statistics parameters, and thus the UID blamed for traffic on the socket will
+ * be the UID that originally created the socket, even if the socket was
+ * subsequently tagged by a different UID.
+ *
+ * Returns 0 on success, or a negative POSIX error code (see errno.h) on
+ * failure.
+ *
+ * Available since API level 33.
+ */
+int android_untag_socket(int sockfd) __INTRODUCED_IN(33);
+
 __END_DECLS
 
 #endif  // ANDROID_MULTINETWORK_H
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 9dc6983..45e8afc 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -256,6 +256,13 @@
      * The hinge angle sensor value is returned in degrees.
      */
     ASENSOR_TYPE_HINGE_ANGLE = 36,
+    /**
+     * {@link ASENSOR_TYPE_HEAD_TRACKER}
+     * reporting-mode: continuous
+     *
+     * Measures the orientation and rotational velocity of a user's head.
+     */
+    ASENSOR_TYPE_HEAD_TRACKER = 37,
 };
 
 /**
@@ -440,6 +447,38 @@
     };
 } AAdditionalInfoEvent;
 
+typedef struct AHeadTrackerEvent {
+    /**
+     * The fields rx, ry, rz are an Euler vector (rotation vector, i.e. a vector
+     * whose direction indicates the axis of rotation and magnitude indicates
+     * the angle to rotate around that axis) representing the transform from
+     * the (arbitrary, possibly slowly drifting) reference frame to the
+     * head frame. Expressed in radians. Magnitude of the vector must be
+     * in the range [0, pi], while the value of individual axes are
+     * in the range [-pi, pi].
+     */
+    float rx;
+    float ry;
+    float rz;
+
+    /**
+     * The fields vx, vy, vz are an Euler vector (rotation vector) representing
+     * the angular velocity of the head (relative to itself), in radians per
+     * second. The direction of this vector indicates the axis of rotation, and
+     * the magnitude indicates the rate of rotation.
+     */
+    float vx;
+    float vy;
+    float vz;
+
+    /**
+     * This value changes each time the reference frame is suddenly and
+     * significantly changed, for example if an orientation filter algorithm
+     * used for determining the orientation has had its state reset.
+     */
+    int32_t discontinuity_count;
+} AHeadTrackerEvent;
+
 /**
  * Information that describes a sensor event, refer to
  * <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional
@@ -476,6 +515,7 @@
             AHeartRateEvent heart_rate;
             ADynamicSensorEvent dynamic_sensor_meta;
             AAdditionalInfoEvent additional_info;
+            AHeadTrackerEvent head_tracker;
         };
         union {
             uint64_t        data[8];
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 3a13104..9a36ecb 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -28,6 +28,7 @@
 
 #include <sys/cdefs.h>
 
+#include <android/choreographer.h>
 #include <android/data_space.h>
 #include <android/hardware_buffer.h>
 #include <android/hdr_metadata.h>
@@ -596,13 +597,23 @@
                                                __INTRODUCED_IN(31);
 
 /**
- * Sets the frame timeline to use.
+ * Sets the frame timeline to use in Surface Flinger.
+ *
+ * A frame timeline should be chosen based on what frame deadline the application
+ * can meet when rendering the frame and the application's desired present time.
+ * By setting a frame timeline, Surface Flinger tries to present the frame at the corresponding
+ * expected present time.
+ *
+ * To receive frame timelines, a callback must be posted to Choreographer using
+ * AChoreographer_postExtendedFrameCallback(). The \a vsnycId can then be extracted from the
+ * callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId().
  *
  * \param vsyncId The vsync ID received from AChoreographer, setting the frame's present target to
- * the corresponding expected present time and deadline from the frame to be rendered.
+ * the corresponding expected present time and deadline from the frame to be rendered. A stale or
+ * invalid value will be ignored.
  */
 void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction,
-                                          int64_t vsyncId) __INTRODUCED_IN(33);
+                                          AVsyncId vsyncId) __INTRODUCED_IN(33);
 
 __END_DECLS
 
diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h
index ae70831..932af2d 100644
--- a/include/ftl/Flags.h
+++ b/include/ftl/Flags.h
@@ -209,12 +209,12 @@
 
 template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
 inline Flags<F> operator~(F f) {
-    return static_cast<F>(~ftl::enum_cast(f));
+    return static_cast<F>(~ftl::to_underlying(f));
 }
 
 template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
 Flags<F> operator|(F lhs, F rhs) {
-    return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs));
+    return static_cast<F>(ftl::to_underlying(lhs) | ftl::to_underlying(rhs));
 }
 
 } // namespace flag_operators
diff --git a/include/ftl/concat.h b/include/ftl/concat.h
new file mode 100644
index 0000000..ded48f7
--- /dev/null
+++ b/include/ftl/concat.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/details/concat.h>
+
+namespace android::ftl {
+
+// Lightweight (not allocating nor sprintf-based) concatenation.
+//
+//   std::string_view name = "Volume";
+//   ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB");
+//
+//   assert(string.str() == "Vol: -3 dB");
+//   assert(string.c_str()[string.size()] == '\0');
+//
+template <std::size_t, typename... Ts>
+struct Concat;
+
+template <std::size_t N, typename T, typename... Ts>
+struct Concat<N, T, Ts...> : Concat<N + details::StaticString<T>::N, Ts...> {
+  explicit constexpr Concat(T v, Ts... args) { append(v, args...); }
+
+ protected:
+  constexpr Concat() = default;
+
+  constexpr void append(T v, Ts... args) {
+    using Str = details::StaticString<T>;
+    const Str str(v);
+
+    // TODO: Replace with constexpr std::copy in C++20.
+    for (auto it = str.view.begin(); it != str.view.end();) {
+      *this->end_++ = *it++;
+    }
+
+    using Base = Concat<N + Str::N, Ts...>;
+    this->Base::append(args...);
+  }
+};
+
+template <std::size_t N>
+struct Concat<N> {
+  static constexpr std::size_t max_size() { return N; }
+  constexpr std::size_t size() const { return end_ - buffer_; }
+
+  constexpr const char* c_str() const { return buffer_; }
+
+  constexpr std::string_view str() const {
+    // TODO: Replace with {buffer_, end_} in C++20.
+    return {buffer_, size()};
+  }
+
+ protected:
+  constexpr Concat() : end_(buffer_) {}
+  constexpr void append() { *end_ = '\0'; }
+
+  char buffer_[N + 1];
+  char* end_;
+};
+
+// Deduction guide.
+template <typename... Ts>
+Concat(Ts&&...) -> Concat<0, Ts...>;
+
+template <std::size_t N>
+constexpr auto truncated(std::string_view v) {
+  return details::Truncated<N>{v};
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/array_traits.h b/include/ftl/details/array_traits.h
similarity index 96%
rename from include/ftl/array_traits.h
rename to include/ftl/details/array_traits.h
index 1265fa1..16e63ec 100644
--- a/include/ftl/array_traits.h
+++ b/include/ftl/details/array_traits.h
@@ -21,9 +21,9 @@
 #include <new>
 #include <type_traits>
 
-#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
+#define FTL_ARRAY_TRAIT(T, U) using U = typename details::ArrayTraits<T>::U
 
-namespace android::ftl {
+namespace android::ftl::details {
 
 template <typename T>
 struct ArrayTraits {
@@ -132,4 +132,4 @@
   }
 };
 
-}  // namespace android::ftl
+}  // namespace android::ftl::details
diff --git a/include/ftl/details/concat.h b/include/ftl/details/concat.h
new file mode 100644
index 0000000..8ce949e
--- /dev/null
+++ b/include/ftl/details/concat.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <string_view>
+
+#include <ftl/string.h>
+
+namespace android::ftl::details {
+
+template <typename T, typename = void>
+struct StaticString;
+
+template <typename T>
+struct StaticString<T, std::enable_if_t<std::is_integral_v<T>>> {
+  static constexpr std::size_t N = to_chars_length_v<T>;
+
+  explicit StaticString(T v) : view(to_chars(buffer, v)) {}
+
+  to_chars_buffer_t<T> buffer;
+  const std::string_view view;
+};
+
+template <std::size_t M>
+struct StaticString<const char (&)[M], void> {
+  static constexpr std::size_t N = M - 1;
+
+  explicit constexpr StaticString(const char (&str)[M]) : view(str, N) {}
+
+  const std::string_view view;
+};
+
+template <std::size_t N>
+struct Truncated {
+  std::string_view view;
+};
+
+template <std::size_t M>
+struct StaticString<Truncated<M>, void> {
+  static constexpr std::size_t N = M;
+
+  explicit constexpr StaticString(Truncated<M> str) : view(str.view.substr(0, N)) {}
+
+  const std::string_view view;
+};
+
+}  // namespace android::ftl::details
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
index dfe3a09..5234c05 100644
--- a/include/ftl/enum.h
+++ b/include/ftl/enum.h
@@ -87,11 +87,13 @@
 
 // Shorthand for casting an enumerator to its integral value.
 //
+// TODO: Replace with std::to_underlying in C++23.
+//
 //   enum class E { A, B, C };
-//   static_assert(ftl::enum_cast(E::B) == 1);
+//   static_assert(ftl::to_underlying(E::B) == 1);
 //
 template <typename E>
-constexpr auto enum_cast(E v) {
+constexpr auto to_underlying(E v) {
   return static_cast<std::underlying_type_t<E>>(v);
 }
 
@@ -137,19 +139,19 @@
 
 template <typename E>
 struct enum_end<E, std::void_t<decltype(E::ftl_last)>> {
-  static constexpr E value = E{enum_cast(E::ftl_last) + 1};
+  static constexpr E value = E{to_underlying(E::ftl_last) + 1};
 };
 
 template <typename E>
 inline constexpr E enum_end_v = enum_end<E>::value;
 
 template <typename E>
-inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1};
+inline constexpr E enum_last_v = E{to_underlying(enum_end_v<E>) - 1};
 
 template <typename E>
 struct enum_size {
-  static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
-  static constexpr auto kEnd = enum_cast(enum_end_v<E>);
+  static constexpr auto kBegin = to_underlying(enum_begin_v<E>);
+  static constexpr auto kEnd = to_underlying(enum_end_v<E>);
   static_assert(kBegin < kEnd, "Invalid range");
 
   static constexpr std::size_t value = kEnd - kBegin;
@@ -174,7 +176,7 @@
 
 template <typename E, template <E> class F, typename T, T... Vs>
 struct EnumRange<E, F, std::integer_sequence<T, Vs...>> {
-  static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+  static constexpr auto kBegin = to_underlying(enum_begin_v<E>);
   static constexpr auto kSize = enum_size_v<E>;
 
   using R = decltype(F<E{}>::value);
@@ -194,7 +196,7 @@
   using E = decltype(I);
   using U = std::underlying_type_t<E>;
 
-  static constexpr E V{U{1} << enum_cast(I)};
+  static constexpr E V{U{1} << to_underlying(I)};
   static constexpr auto value = ftl_enum<E, V>();
 };
 
@@ -237,10 +239,10 @@
 //
 template <typename E>
 constexpr std::optional<std::string_view> enum_name(E v) {
-  const auto value = enum_cast(v);
+  const auto value = to_underlying(v);
 
-  constexpr auto kBegin = enum_cast(enum_begin_v<E>);
-  constexpr auto kLast = enum_cast(enum_last_v<E>);
+  constexpr auto kBegin = to_underlying(enum_begin_v<E>);
+  constexpr auto kLast = to_underlying(enum_last_v<E>);
   if (value < kBegin || value > kLast) return {};
 
   constexpr auto kRange = details::EnumRange<E, details::EnumName>{};
@@ -256,7 +258,7 @@
 //
 template <typename E>
 constexpr std::optional<std::string_view> flag_name(E v) {
-  const auto value = enum_cast(v);
+  const auto value = to_underlying(v);
 
   // TODO: Replace with std::popcount and std::countr_zero in C++20.
   if (__builtin_popcountl(value) != 1) return {};
@@ -277,7 +279,7 @@
   if (const auto name = enum_name(v)) {
     return std::string(*name);
   }
-  return to_string(enum_cast(v));
+  return to_string(to_underlying(v));
 }
 
 // Returns a stringified flag enumerator, or its integral value if not named.
@@ -293,7 +295,7 @@
     return std::string(*name);
   }
   constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex;
-  return to_string(enum_cast(v), radix);
+  return to_string(to_underlying(v), radix);
 }
 
 }  // namespace android::ftl
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
index 65a9536..03587e3 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <ftl/array_traits.h>
+#include <ftl/details/array_traits.h>
 #include <ftl/static_vector.h>
 
 #include <algorithm>
@@ -73,7 +73,7 @@
 //   assert(strings[2] == "???");
 //
 template <typename T, std::size_t N>
-class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
+class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<SmallVector> {
   using Static = StaticVector<T, N>;
   using Dynamic = SmallVector<T, 0>;
 
@@ -266,12 +266,12 @@
 
 // Partial specialization without static storage.
 template <typename T>
-class SmallVector<T, 0> final : ArrayTraits<T>,
-                                ArrayIterators<SmallVector<T, 0>, T>,
+class SmallVector<T, 0> final : details::ArrayTraits<T>,
+                                details::ArrayIterators<SmallVector<T, 0>, T>,
                                 std::vector<T> {
-  using ArrayTraits<T>::construct_at;
+  using details::ArrayTraits<T>::construct_at;
 
-  using Iter = ArrayIterators<SmallVector, T>;
+  using Iter = details::ArrayIterators<SmallVector, T>;
   using Impl = std::vector<T>;
 
   friend Iter;
diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h
index cd7b92a..b7f8c29 100644
--- a/include/ftl/static_vector.h
+++ b/include/ftl/static_vector.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <ftl/array_traits.h>
+#include <ftl/details/array_traits.h>
 #include <ftl/initializer_list.h>
 
 #include <algorithm>
@@ -73,14 +73,14 @@
 //   assert(strings[2] == "???");
 //
 template <typename T, std::size_t N>
-class StaticVector final : ArrayTraits<T>,
-                           ArrayIterators<StaticVector<T, N>, T>,
-                           ArrayComparators<StaticVector> {
+class StaticVector final : details::ArrayTraits<T>,
+                           details::ArrayIterators<StaticVector<T, N>, T>,
+                           details::ArrayComparators<StaticVector> {
   static_assert(N > 0);
 
-  using ArrayTraits<T>::construct_at;
+  using details::ArrayTraits<T>::construct_at;
 
-  using Iter = ArrayIterators<StaticVector, T>;
+  using Iter = details::ArrayIterators<StaticVector, T>;
   friend Iter;
 
   // There is ambiguity when constructing from two iterator-like elements like pointers:
diff --git a/include/input/Input.h b/include/input/Input.h
index ddff144..f4147a0 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -31,10 +31,8 @@
 #include <stdint.h>
 #include <ui/Transform.h>
 #include <utils/BitSet.h>
-#include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
-#include <utils/Vector.h>
 #include <array>
 #include <limits>
 #include <queue>
@@ -88,7 +86,7 @@
      */
     AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40,
 
-#ifdef __linux__
+#if defined(__linux__)
     /**
      * This event was generated or modified by accessibility service.
      */
@@ -799,11 +797,11 @@
 
     // Low-level accessors.
     inline const PointerProperties* getPointerProperties() const {
-        return mPointerProperties.array();
+        return mPointerProperties.data();
     }
     inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.data(); }
     inline const PointerCoords* getSamplePointerCoords() const {
-            return mSamplePointerCoords.array();
+        return mSamplePointerCoords.data();
     }
 
     static const char* getLabel(int32_t axis);
@@ -834,9 +832,9 @@
     float mRawYCursorPosition;
     ui::Transform mRawTransform;
     nsecs_t mDownTime;
-    Vector<PointerProperties> mPointerProperties;
+    std::vector<PointerProperties> mPointerProperties;
     std::vector<nsecs_t> mSampleEventTimes;
-    Vector<PointerCoords> mSamplePointerCoords;
+    std::vector<PointerCoords> mSamplePointerCoords;
 };
 
 /*
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 451ca3c..f6f8939 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -84,7 +84,7 @@
 
     const std::string getLoadFileName() const;
 
-    /* Combines this key character map with an overlay. */
+    /* Combines this key character map with the provided overlay. */
     void combine(const KeyCharacterMap& overlay);
 
     /* Gets the keyboard type. */
@@ -144,6 +144,8 @@
 
     bool operator==(const KeyCharacterMap& other) const;
 
+    bool operator!=(const KeyCharacterMap& other) const;
+
     KeyCharacterMap(const KeyCharacterMap& other);
 
     virtual ~KeyCharacterMap();
@@ -230,11 +232,12 @@
     KeyedVector<int32_t, Key*> mKeys;
     KeyboardType mType;
     std::string mLoadFileName;
+    bool mLayoutOverlayApplied;
 
     KeyedVector<int32_t, int32_t> mKeysByScanCode;
     KeyedVector<int32_t, int32_t> mKeysByUsageCode;
 
-    KeyCharacterMap();
+    KeyCharacterMap(const std::string& filename);
 
     bool getKey(int32_t keyCode, const Key** outKey) const;
     bool getKeyBehavior(int32_t keyCode, int32_t metaState,
@@ -243,8 +246,6 @@
 
     bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
 
-    static base::Result<std::shared_ptr<KeyCharacterMap>> load(Tokenizer* tokenizer, Format format);
-
     static void addKey(Vector<KeyEvent>& outEvents,
             int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
     static void addMetaKeys(Vector<KeyEvent>& outEvents,
@@ -264,6 +265,15 @@
             int32_t deviceId, int32_t metaState, nsecs_t time,
             int32_t keyCode, int32_t keyMetaState,
             int32_t* currentMetaState);
+
+    /* Clears all data stored in this key character map */
+    void clear();
+
+    /* Loads the KeyCharacterMap provided by the tokenizer into this instance. */
+    status_t load(Tokenizer* tokenizer, Format format);
+
+    /* Reloads the data from mLoadFileName and unapplies any overlay. */
+    status_t reloadBaseFromFile();
 };
 
 } // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 9abe4b5..269b086 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -320,11 +320,6 @@
                 //
                 // Note that this is not race-free if the context manager
                 // dies while this code runs.
-                //
-                // TODO: add a driver API to wait for context manager, or
-                // stop special casing handle 0 for context manager and add
-                // a driver API to get a handle to the context manager with
-                // proper reference counting.
 
                 IPCThreadState* ipc = IPCThreadState::self();
 
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index f5abb85..7067830 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -93,20 +93,20 @@
 
 // ----------------------------------------------------------------------
 
-#define DECLARE_META_INTERFACE(INTERFACE)                               \
-public:                                                                 \
-    static const ::android::String16 descriptor;                        \
-    static ::android::sp<I##INTERFACE> asInterface(                     \
-            const ::android::sp<::android::IBinder>& obj);              \
-    virtual const ::android::String16& getInterfaceDescriptor() const;  \
-    I##INTERFACE();                                                     \
-    virtual ~I##INTERFACE();                                            \
-    static bool setDefaultImpl(std::unique_ptr<I##INTERFACE> impl);     \
-    static const std::unique_ptr<I##INTERFACE>& getDefaultImpl();       \
-private:                                                                \
-    static std::unique_ptr<I##INTERFACE> default_impl;                  \
-public:                                                                 \
-
+#define DECLARE_META_INTERFACE(INTERFACE)                                                         \
+public:                                                                                           \
+    static const ::android::String16 descriptor;                                                  \
+    static ::android::sp<I##INTERFACE> asInterface(const ::android::sp<::android::IBinder>& obj); \
+    virtual const ::android::String16& getInterfaceDescriptor() const;                            \
+    I##INTERFACE();                                                                               \
+    virtual ~I##INTERFACE();                                                                      \
+    static bool setDefaultImpl(::android::sp<I##INTERFACE> impl);                                 \
+    static const ::android::sp<I##INTERFACE>& getDefaultImpl();                                   \
+                                                                                                  \
+private:                                                                                          \
+    static ::android::sp<I##INTERFACE> default_impl;                                              \
+                                                                                                  \
+public:
 
 #define __IINTF_CONCAT(x, y) (x ## y)
 
@@ -142,8 +142,8 @@
         }                                                                                          \
         return intr;                                                                               \
     }                                                                                              \
-    std::unique_ptr<ITYPE> ITYPE::default_impl;                                                    \
-    bool ITYPE::setDefaultImpl(std::unique_ptr<ITYPE> impl) {                                      \
+    ::android::sp<ITYPE> ITYPE::default_impl;                                                      \
+    bool ITYPE::setDefaultImpl(::android::sp<ITYPE> impl) {                                        \
         /* Only one user of this interface can use this function     */                            \
         /* at a time. This is a heuristic to detect if two different */                            \
         /* users in the same process use this function.              */                            \
@@ -154,7 +154,7 @@
         }                                                                                          \
         return false;                                                                              \
     }                                                                                              \
-    const std::unique_ptr<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; }          \
+    const ::android::sp<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; }            \
     ITYPE::INAME() {}                                                                              \
     ITYPE::~INAME() {}
 
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index c903998..2c471c6 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -163,6 +163,15 @@
     const T get() const { return mT; }
 
     /**
+     * Release the underlying resource.
+     */
+    [[nodiscard]] T release() {
+        T a = mT;
+        mT = DEFAULT;
+        return a;
+    }
+
+    /**
      * This allows the value in this class to be set from beneath it. If you call this method and
      * then change the value of T*, you must take ownership of the value you are replacing and add
      * ownership to the object that is put in here.
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 58fa13a..357b454 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -752,6 +752,29 @@
     ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass));
 }
 
+static void addOne(int* to) {
+    if (!to) return;
+    ++(*to);
+}
+struct FakeResource : public ndk::impl::ScopedAResource<int*, addOne, nullptr> {
+    explicit FakeResource(int* a) : ScopedAResource(a) {}
+};
+
+TEST(NdkBinder_ScopedAResource, GetDelete) {
+    int deleteCount = 0;
+    { FakeResource resource(&deleteCount); }
+    EXPECT_EQ(deleteCount, 1);
+}
+
+TEST(NdkBinder_ScopedAResource, Release) {
+    int deleteCount = 0;
+    {
+        FakeResource resource(&deleteCount);
+        (void)resource.release();
+    }
+    EXPECT_EQ(deleteCount, 0);
+}
+
 int main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index e4df98a..90cbf9d 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -32,7 +32,7 @@
         "com.android.uwb",
         "com.android.virt",
     ],
-    min_sdk_version: "current",
+    min_sdk_version: "Tiramisu",
 }
 
 rust_library {
@@ -79,7 +79,7 @@
         "com.android.uwb",
         "com.android.virt",
     ],
-    min_sdk_version: "current",
+    min_sdk_version: "Tiramisu",
     lints: "none",
     clippy_lints: "none",
 }
@@ -137,7 +137,7 @@
         "com.android.uwb",
         "com.android.virt",
     ],
-    min_sdk_version: "current",
+    min_sdk_version: "Tiramisu",
 }
 
 // TODO(b/184872979): remove once the Rust API is created.
@@ -154,7 +154,7 @@
         "com.android.uwb",
         "com.android.virt",
     ],
-    min_sdk_version: "current",
+    min_sdk_version: "Tiramisu",
 }
 
 rust_test {
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 91047be..9dcef42 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -28,8 +28,8 @@
 //!
 //! [`Tokio`]: crate::Tokio
 
-use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
-use binder::{FromIBinder, StatusCode};
+use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong};
+use binder::binder_impl::BinderAsyncRuntime;
 use std::future::Future;
 
 /// Retrieve an existing service for a particular interface, sleeping for a few
@@ -37,12 +37,12 @@
 pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
     if binder::is_handling_transaction() {
         // See comment in the BinderAsyncPool impl.
-        return binder::public_api::get_interface::<T>(name);
+        return binder::get_interface::<T>(name);
     }
 
     let name = name.to_string();
     let res = tokio::task::spawn_blocking(move || {
-        binder::public_api::get_interface::<T>(&name)
+        binder::get_interface::<T>(&name)
     }).await;
 
     // The `is_panic` branch is not actually reachable in Android as we compile
@@ -61,12 +61,12 @@
 pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
     if binder::is_handling_transaction() {
         // See comment in the BinderAsyncPool impl.
-        return binder::public_api::wait_for_interface::<T>(name);
+        return binder::wait_for_interface::<T>(name);
     }
 
     let name = name.to_string();
     let res = tokio::task::spawn_blocking(move || {
-        binder::public_api::wait_for_interface::<T>(&name)
+        binder::wait_for_interface::<T>(&name)
     }).await;
 
     // The `is_panic` branch is not actually reachable in Android as we compile
@@ -120,3 +120,24 @@
         }
     }
 }
+
+/// Wrapper around Tokio runtime types for providing a runtime to a binder server.
+pub struct TokioRuntime<R>(pub R);
+
+impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Runtime> {
+    fn block_on<F: Future>(&self, future: F) -> F::Output {
+        self.0.block_on(future)
+    }
+}
+
+impl BinderAsyncRuntime for TokioRuntime<std::sync::Arc<tokio::runtime::Runtime>> {
+    fn block_on<F: Future>(&self, future: F) -> F::Output {
+        self.0.block_on(future)
+    }
+}
+
+impl BinderAsyncRuntime for TokioRuntime<tokio::runtime::Handle> {
+    fn block_on<F: Future>(&self, future: F) -> F::Output {
+        self.0.block_on(future)
+    }
+}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 4d6b294..467e51e 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -192,9 +192,6 @@
     /// Is this object still alive?
     fn is_binder_alive(&self) -> bool;
 
-    /// Send a ping transaction to this object
-    fn ping_binder(&mut self) -> Result<()>;
-
     /// Indicate that the service intends to receive caller security contexts.
     #[cfg(not(android_vndk))]
     fn set_requesting_sid(&mut self, enable: bool);
@@ -270,6 +267,9 @@
     /// The recipient will no longer be called if this object
     /// dies.
     fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
+
+    /// Send a ping transaction to this object
+    fn ping_binder(&mut self) -> Result<()>;
 }
 
 /// Opaque reference to the type of a Binder interface.
@@ -536,13 +536,13 @@
 /// ```
 macro_rules! binder_fn_get_class {
     ($class:ty) => {
-        binder_fn_get_class!($crate::InterfaceClass::new::<$class>());
+        binder_fn_get_class!($crate::binder_impl::InterfaceClass::new::<$class>());
     };
 
     ($constructor:expr) => {
-        fn get_class() -> $crate::InterfaceClass {
+        fn get_class() -> $crate::binder_impl::InterfaceClass {
             static CLASS_INIT: std::sync::Once = std::sync::Once::new();
-            static mut CLASS: Option<$crate::InterfaceClass> = None;
+            static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
 
             CLASS_INIT.call_once(|| unsafe {
                 // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
@@ -772,7 +772,7 @@
                 native: $native($on_transact),
                 proxy: $proxy {},
                 $(async: $async_interface,)?
-                stability: $crate::Stability::default(),
+                stability: $crate::binder_impl::Stability::default(),
             }
         }
     };
@@ -811,7 +811,7 @@
                     $($fname: $fty = $finit),*
                 },
                 $(async: $async_interface,)?
-                stability: $crate::Stability::default(),
+                stability: $crate::binder_impl::Stability::default(),
             }
         }
     };
@@ -828,9 +828,9 @@
     } => {
         $crate::declare_binder_interface! {
             $interface[$descriptor] {
-                @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")]
+                @doc[concat!("A binder [`Remotable`]($crate::binder_impl::Remotable) that holds an [`", stringify!($interface), "`] object.")]
                 native: $native($on_transact),
-                @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
+                @doc[concat!("A binder [`Proxy`]($crate::binder_impl::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")]
                 proxy: $proxy {
                     $($fname: $fty = $finit),*
                 },
@@ -867,7 +867,7 @@
             }
         }
 
-        impl $crate::Proxy for $proxy
+        impl $crate::binder_impl::Proxy for $proxy
         where
             $proxy: $interface,
         {
@@ -875,7 +875,7 @@
                 $descriptor
             }
 
-            fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> {
+            fn from_binder(mut binder: $crate::SpIBinder) -> std::result::Result<Self, $crate::StatusCode> {
                 Ok(Self { binder, $($fname: $finit),* })
             }
         }
@@ -887,19 +887,19 @@
         impl $native {
             /// Create a new binder service.
             pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
-                let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+                let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability);
                 #[cfg(not(android_vndk))]
-                $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
+                $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
                 $crate::Strong::new(Box::new(binder))
             }
         }
 
-        impl $crate::Remotable for $native {
+        impl $crate::binder_impl::Remotable for $native {
             fn get_descriptor() -> &'static str {
                 $descriptor
             }
 
-            fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> {
+            fn on_transact(&self, code: $crate::binder_impl::TransactionCode, data: &$crate::binder_impl::BorrowedParcel<'_>, reply: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 match $on_transact(&*self.0, code, data, reply) {
                     // The C++ backend converts UNEXPECTED_NULL into an exception
                     Err($crate::StatusCode::UNEXPECTED_NULL) => {
@@ -913,19 +913,19 @@
                 }
             }
 
-            fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> $crate::Result<()> {
+            fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> std::result::Result<(), $crate::StatusCode> {
                 self.0.dump(file, args)
             }
 
-            fn get_class() -> $crate::InterfaceClass {
+            fn get_class() -> $crate::binder_impl::InterfaceClass {
                 static CLASS_INIT: std::sync::Once = std::sync::Once::new();
-                static mut CLASS: Option<$crate::InterfaceClass> = None;
+                static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None;
 
                 CLASS_INIT.call_once(|| unsafe {
                     // Safety: This assignment is guarded by the `CLASS_INIT` `Once`
                     // variable, and therefore is thread-safe, as it can only occur
                     // once.
-                    CLASS = Some($crate::InterfaceClass::new::<$crate::Binder<$native>>());
+                    CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>());
                 });
                 unsafe {
                     // Safety: The `CLASS` variable can only be mutated once, above,
@@ -936,25 +936,25 @@
         }
 
         impl $crate::FromIBinder for dyn $interface {
-            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> {
-                use $crate::AssociateClass;
+            fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $interface>, $crate::StatusCode> {
+                use $crate::binder_impl::AssociateClass;
 
                 let existing_class = ibinder.get_class();
                 if let Some(class) = existing_class {
-                    if class != <$native as $crate::Remotable>::get_class() &&
-                        class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+                    if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+                        class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
                     {
                         // The binder object's descriptor string matches what we
                         // expect. We still need to treat this local or already
                         // associated object as remote, because we can't cast it
                         // into a Rust service object without a matching class
                         // pointer.
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
-                if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
-                    let service: $crate::Result<$crate::Binder<$native>> =
+                if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+                    let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
                         std::convert::TryFrom::try_from(ibinder.clone());
                     if let Ok(service) = service {
                         // We were able to associate with our expected class and
@@ -962,7 +962,7 @@
                         return Ok($crate::Strong::new(Box::new(service)));
                     } else {
                         // Service is remote
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -970,18 +970,18 @@
             }
         }
 
-        impl $crate::parcel::Serialize for dyn $interface + '_
+        impl $crate::binder_impl::Serialize for dyn $interface + '_
         where
             dyn $interface: $crate::Interface
         {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let binder = $crate::Interface::as_binder(self);
                 parcel.write(&binder)
             }
         }
 
-        impl $crate::parcel::SerializeOption for dyn $interface + '_ {
-            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::SerializeOption for dyn $interface + '_ {
+            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&this.map($crate::Interface::as_binder))
             }
         }
@@ -1004,25 +1004,25 @@
         $(
         // Async interface trait implementations.
         impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
-            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> {
-                use $crate::AssociateClass;
+            fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> {
+                use $crate::binder_impl::AssociateClass;
 
                 let existing_class = ibinder.get_class();
                 if let Some(class) = existing_class {
-                    if class != <$native as $crate::Remotable>::get_class() &&
-                        class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+                    if class != <$native as $crate::binder_impl::Remotable>::get_class() &&
+                        class.get_descriptor() == <$native as $crate::binder_impl::Remotable>::get_descriptor()
                     {
                         // The binder object's descriptor string matches what we
                         // expect. We still need to treat this local or already
                         // associated object as remote, because we can't cast it
                         // into a Rust service object without a matching class
                         // pointer.
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
-                if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
-                    let service: $crate::Result<$crate::Binder<$native>> =
+                if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
+                    let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
                         std::convert::TryFrom::try_from(ibinder.clone());
                     if let Ok(service) = service {
                         // We were able to associate with our expected class and
@@ -1031,7 +1031,7 @@
                         //return Ok($crate::Strong::new(Box::new(service)));
                     } else {
                         // Service is remote
-                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -1039,15 +1039,15 @@
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let binder = $crate::Interface::as_binder(self);
                 parcel.write(&binder)
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ {
-            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
+            fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&this.map($crate::Interface::as_binder))
             }
         }
@@ -1067,11 +1067,11 @@
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::ToAsyncInterface<P> for dyn $interface {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
             type Target = dyn $async_interface<P>;
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::ToSyncInterface for dyn $async_interface<P> {
+        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
             type Target = dyn $interface;
         }
         )?
@@ -1103,29 +1103,29 @@
             }
         }
 
-        impl $crate::parcel::Serialize for $enum {
-            fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::Serialize for $enum {
+            fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&self.0)
             }
         }
 
-        impl $crate::parcel::SerializeArray for $enum {
-            fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+        impl $crate::binder_impl::SerializeArray for $enum {
+            fn serialize_array(slice: &[Self], parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
-                <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
+                <$backing as $crate::binder_impl::SerializeArray>::serialize_array(&v[..], parcel)
             }
         }
 
-        impl $crate::parcel::Deserialize for $enum {
-            fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> {
+        impl $crate::binder_impl::Deserialize for $enum {
+            fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Self, $crate::StatusCode> {
                 parcel.read().map(Self)
             }
         }
 
-        impl $crate::parcel::DeserializeArray for $enum {
-            fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> {
+        impl $crate::binder_impl::DeserializeArray for $enum {
+            fn deserialize_array(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Option<Vec<Self>>, $crate::StatusCode> {
                 let v: Option<Vec<$backing>> =
-                    <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
+                    <$backing as $crate::binder_impl::DeserializeArray>::deserialize_array(parcel)?;
                 Ok(v.map(|v| v.into_iter().map(Self).collect()))
             }
         }
diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs
index 214c0b5..579f9f9 100644
--- a/libs/binder/rust/src/binder_async.rs
+++ b/libs/binder/rust/src/binder_async.rs
@@ -53,3 +53,9 @@
         B: Send + 'a,
         E: From<crate::StatusCode>;
 }
+
+/// A runtime for executing an async binder server.
+pub trait BinderAsyncRuntime {
+    /// Block on the provided future, running it to completion and returning its output.
+    fn block_on<F: Future>(&self, future: F) -> F::Output;
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 7c04a72..1d7de98 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -101,45 +101,50 @@
 mod binder_async;
 mod error;
 mod native;
+mod parcel;
 mod state;
 
 use binder_ndk_sys as sys;
 
-pub mod parcel;
-
-pub use crate::binder::{
-    BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
-    Stability, Strong, ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, Weak,
-    FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
+pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
+pub use error::{ExceptionCode, Status, StatusCode};
+pub use native::{
+    add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
 };
-pub use crate::binder_async::{BoxFuture, BinderAsyncPool};
-pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder};
-pub use parcel::{BorrowedParcel, Parcel};
-pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
-pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
+pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
+pub use proxy::{
+    get_interface, get_service, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder,
+    WpIBinder,
+};
 pub use state::{ProcessState, ThreadState};
 
+/// Binder result containing a [`Status`] on error.
+pub type Result<T> = std::result::Result<T, Status>;
+
+/// Advanced Binder APIs needed internally by AIDL or when manually using Binder
+/// without AIDL.
+pub mod binder_impl {
+    pub use crate::binder::{
+        IBinderInternal, InterfaceClass, Remotable, Stability, ToAsyncInterface, ToSyncInterface,
+        TransactionCode, TransactionFlags, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
+        FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+    };
+    pub use crate::binder_async::BinderAsyncRuntime;
+    pub use crate::error::status_t;
+    pub use crate::native::Binder;
+    pub use crate::parcel::{
+        BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Parcel,
+        ParcelableMetadata, Serialize, SerializeArray, SerializeOption, NON_NULL_PARCELABLE_FLAG,
+        NULL_PARCELABLE_FLAG,
+    };
+    pub use crate::proxy::{AssociateClass, Proxy};
+}
+
 /// Unstable, in-development API that only allowlisted clients are allowed to use.
+#[doc(hidden)]
 pub mod unstable_api {
     pub use crate::binder::AsNative;
     pub use crate::proxy::unstable_api::new_spibinder;
     pub use crate::sys::AIBinder;
 }
-
-/// The public API usable outside AIDL-generated interface crates.
-pub mod public_api {
-    pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder};
-    pub use super::{
-        add_service, force_lazy_services_persist, get_interface, register_lazy_service,
-        wait_for_interface,
-    };
-    pub use super::{
-        BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder,
-        Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak,
-        WpIBinder,
-    };
-
-    /// Binder result containing a [`Status`] on error.
-    pub type Result<T> = std::result::Result<T, Status>;
-}
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 206b90c..256fa8b 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -496,7 +496,7 @@
     {
         let start = self.get_data_position();
         let parcelable_size: i32 = self.read()?;
-        if parcelable_size < 0 {
+        if parcelable_size < 4 {
             return Err(StatusCode::BAD_VALUE);
         }
 
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 61f88b6..0c7e48d 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -802,35 +802,32 @@
 #[macro_export]
 macro_rules! impl_serialize_for_parcelable {
     ($parcelable:ident) => {
-        impl $crate::parcel::Serialize for $parcelable {
+        impl $crate::binder_impl::Serialize for $parcelable {
             fn serialize(
                 &self,
-                parcel: &mut $crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
-                <Self as $crate::parcel::SerializeOption>::serialize_option(
-                    Some(self),
-                    parcel,
-                )
+                parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
+                <Self as $crate::binder_impl::SerializeOption>::serialize_option(Some(self), parcel)
             }
         }
 
-        impl $crate::parcel::SerializeArray for $parcelable {}
+        impl $crate::binder_impl::SerializeArray for $parcelable {}
 
-        impl $crate::parcel::SerializeOption for $parcelable {
+        impl $crate::binder_impl::SerializeOption for $parcelable {
             fn serialize_option(
                 this: Option<&Self>,
-                parcel: &mut $crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &mut $crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 if let Some(this) = this {
-                    use $crate::parcel::Parcelable;
-                    parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?;
+                    use $crate::Parcelable;
+                    parcel.write(&$crate::binder_impl::NON_NULL_PARCELABLE_FLAG)?;
                     this.write_to_parcel(parcel)
                 } else {
-                    parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG)
+                    parcel.write(&$crate::binder_impl::NULL_PARCELABLE_FLAG)
                 }
             }
         }
-    }
+    };
 }
 
 /// Implement `Deserialize` trait and friends for a parcelable
@@ -842,54 +839,54 @@
 #[macro_export]
 macro_rules! impl_deserialize_for_parcelable {
     ($parcelable:ident) => {
-        impl $crate::parcel::Deserialize for $parcelable {
+        impl $crate::binder_impl::Deserialize for $parcelable {
             fn deserialize(
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<Self> {
-                $crate::parcel::DeserializeOption::deserialize_option(parcel)
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<Self, $crate::StatusCode> {
+                $crate::binder_impl::DeserializeOption::deserialize_option(parcel)
                     .transpose()
                     .unwrap_or(Err($crate::StatusCode::UNEXPECTED_NULL))
             }
             fn deserialize_from(
                 &mut self,
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 let status: i32 = parcel.read()?;
-                if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+                if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
                     Err($crate::StatusCode::UNEXPECTED_NULL)
                 } else {
-                    use $crate::parcel::Parcelable;
+                    use $crate::Parcelable;
                     self.read_from_parcel(parcel)
                 }
             }
         }
 
-        impl $crate::parcel::DeserializeArray for $parcelable {}
+        impl $crate::binder_impl::DeserializeArray for $parcelable {}
 
-        impl $crate::parcel::DeserializeOption for $parcelable {
+        impl $crate::binder_impl::DeserializeOption for $parcelable {
             fn deserialize_option(
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<Option<Self>> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<Option<Self>, $crate::StatusCode> {
                 let mut result = None;
                 Self::deserialize_option_from(&mut result, parcel)?;
                 Ok(result)
             }
             fn deserialize_option_from(
                 this: &mut Option<Self>,
-                parcel: &$crate::parcel::BorrowedParcel<'_>,
-            ) -> $crate::Result<()> {
+                parcel: &$crate::binder_impl::BorrowedParcel<'_>,
+            ) -> std::result::Result<(), $crate::StatusCode> {
                 let status: i32 = parcel.read()?;
-                if status == $crate::parcel::NULL_PARCELABLE_FLAG {
+                if status == $crate::binder_impl::NULL_PARCELABLE_FLAG {
                     *this = None;
                     Ok(())
                 } else {
-                    use $crate::parcel::Parcelable;
+                    use $crate::Parcelable;
                     this.get_or_insert_with(Self::default)
                         .read_from_parcel(parcel)
                 }
             }
         }
-    }
+    };
 }
 
 impl<T: Serialize> Serialize for Box<T> {
@@ -918,7 +915,7 @@
 
 #[cfg(test)]
 mod tests {
-    use crate::Parcel;
+    use crate::parcel::Parcel;
     use super::*;
 
     #[test]
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index b4282b2..d58e839 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -15,9 +15,11 @@
  */
 
 use crate::binder::Stability;
-use crate::error::{Result, StatusCode};
-use crate::parcel::{Parcel, BorrowedParcel, Parcelable};
-use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
+use crate::error::StatusCode;
+use crate::parcel::{
+    BorrowedParcel, Deserialize, Parcel, Parcelable, Serialize, NON_NULL_PARCELABLE_FLAG,
+    NULL_PARCELABLE_FLAG,
+};
 
 use downcast_rs::{impl_downcast, DowncastSync};
 use std::any::Any;
@@ -53,12 +55,6 @@
     Parcel(Parcel),
 }
 
-impl Default for ParcelableHolderData {
-    fn default() -> Self {
-        ParcelableHolderData::Empty
-    }
-}
-
 /// A container that can hold any arbitrary `Parcelable`.
 ///
 /// This type is currently used for AIDL parcelable fields.
@@ -66,7 +62,7 @@
 /// `ParcelableHolder` is currently not thread-safe (neither
 /// `Send` nor `Sync`), mainly because it internally contains
 /// a `Parcel` which in turn is not thread-safe.
-#[derive(Debug, Default)]
+#[derive(Debug)]
 pub struct ParcelableHolder {
     // This is a `Mutex` because of `get_parcelable`
     // which takes `&self` for consistency with C++.
@@ -97,7 +93,7 @@
     }
 
     /// Set the parcelable contained in this `ParcelableHolder`.
-    pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<()>
+    pub fn set_parcelable<T>(&mut self, p: Arc<T>) -> Result<(), StatusCode>
     where
         T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
     {
@@ -126,7 +122,7 @@
     /// * `Ok(None)` if the holder is empty or the descriptor does not match
     /// * `Ok(Some(_))` if the object holds a parcelable of type `T`
     ///   with the correct descriptor
-    pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>>
+    pub fn get_parcelable<T>(&self) -> Result<Option<Arc<T>>, StatusCode>
     where
         T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug + Send + Sync,
     {
@@ -176,11 +172,28 @@
     }
 }
 
-impl_serialize_for_parcelable!(ParcelableHolder);
-impl_deserialize_for_parcelable!(ParcelableHolder);
+impl Serialize for ParcelableHolder {
+    fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
+        parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
+        self.write_to_parcel(parcel)
+    }
+}
+
+impl Deserialize for ParcelableHolder {
+    fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self, StatusCode> {
+        let status: i32 = parcel.read()?;
+        if status == NULL_PARCELABLE_FLAG {
+            Err(StatusCode::UNEXPECTED_NULL)
+        } else {
+            let mut parcelable = ParcelableHolder::new(Default::default());
+            parcelable.read_from_parcel(parcel)?;
+            Ok(parcelable)
+        }
+    }
+}
 
 impl Parcelable for ParcelableHolder {
-    fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+    fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
         parcel.write(&self.stability)?;
 
         let mut data = self.data.lock().unwrap();
@@ -219,7 +232,7 @@
         }
     }
 
-    fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
+    fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> {
         self.stability = parcel.read()?;
 
         let data_size: i32 = parcel.read()?;
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 760d862..12bfde7 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -312,17 +312,6 @@
         }
     }
 
-    fn ping_binder(&mut self) -> Result<()> {
-        let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `self` always contains a
-            // valid pointer to an `AIBinder`.
-            //
-            // This call does not affect ownership of its pointer parameter.
-            sys::AIBinder_ping(self.as_native_mut())
-        };
-        status_result(status)
-    }
-
     #[cfg(not(android_vndk))]
     fn set_requesting_sid(&mut self, enable: bool) {
         unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
@@ -412,6 +401,17 @@
             )
         })
     }
+
+    fn ping_binder(&mut self) -> Result<()> {
+        let status = unsafe {
+            // Safety: `SpIBinder` guarantees that `self` always contains a
+            // valid pointer to an `AIBinder`.
+            //
+            // This call does not affect ownership of its pointer parameter.
+            sys::AIBinder_ping(self.as_native_mut())
+        };
+        status_result(status)
+    }
 }
 
 impl Serialize for SpIBinder {
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 80dc476..50daf1c 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -17,11 +17,13 @@
 //! Rust Binder crate integration tests
 
 use binder::{declare_binder_enum, declare_binder_interface};
-use binder::parcel::BorrowedParcel;
-use binder::{
-    Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
-    FIRST_CALL_TRANSACTION,
+use binder::{BinderFeatures, Interface, StatusCode, ThreadState};
+// Import from internal API for testing only, do not use this module in
+// production.
+use binder::binder_impl::{
+    Binder, BorrowedParcel, IBinderInternal, TransactionCode, FIRST_CALL_TRANSACTION,
 };
+
 use std::convert::{TryFrom, TryInto};
 use std::ffi::CStr;
 use std::fs::File;
@@ -120,7 +122,7 @@
 }
 
 impl Interface for TestService {
-    fn dump(&self, _file: &File, args: &[&CStr]) -> binder::Result<()> {
+    fn dump(&self, _file: &File, args: &[&CStr]) -> Result<(), StatusCode> {
         let mut dump_args = self.dump_args.lock().unwrap();
         dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned()));
         Ok(())
@@ -128,22 +130,22 @@
 }
 
 impl ITest for TestService {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         Ok(self.s.clone())
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         let args = self.dump_args.lock().unwrap().clone();
         Ok(args)
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         let sid =
             ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
         sid.ok_or(StatusCode::UNEXPECTED_NULL)
     }
 
-    fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
         Ok(binder::is_handling_transaction())
     }
 }
@@ -151,31 +153,31 @@
 /// Trivial testing binder interface
 pub trait ITest: Interface {
     /// Returns a test string
-    fn test(&self) -> binder::Result<String>;
+    fn test(&self) -> Result<String, StatusCode>;
 
     /// Return the arguments sent via dump
-    fn get_dump_args(&self) -> binder::Result<Vec<String>>;
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode>;
 
     /// Returns the caller's SELinux context
-    fn get_selinux_context(&self) -> binder::Result<String>;
+    fn get_selinux_context(&self) -> Result<String, StatusCode>;
 
     /// Returns the value of calling `is_handling_transaction`.
-    fn get_is_handling_transaction(&self) -> binder::Result<bool>;
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode>;
 }
 
 /// Async trivial testing binder interface
 pub trait IATest<P>: Interface {
     /// Returns a test string
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
 
     /// Return the arguments sent via dump
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>;
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>>;
 
     /// Returns the caller's SELinux context
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>>;
 
     /// Returns the value of calling `is_handling_transaction`.
-    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>>;
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>>;
 }
 
 declare_binder_interface! {
@@ -193,7 +195,7 @@
     code: TransactionCode,
     _data: &BorrowedParcel<'_>,
     reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
     match code.try_into()? {
         TestTransactionCode::Test => reply.write(&service.test()?),
         TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?),
@@ -203,21 +205,21 @@
 }
 
 impl ITest for BpTest {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         let reply =
             self.binder
                 .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
         reply.read()
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         let reply =
             self.binder
                 .transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(()))?;
         reply.read()
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         let reply = self.binder.transact(
             TestTransactionCode::GetSelinuxContext as TransactionCode,
             0,
@@ -226,7 +228,7 @@
         reply.read()
     }
 
-    fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
         let reply = self.binder.transact(
             TestTransactionCode::GetIsHandlingTransaction as TransactionCode,
             0,
@@ -237,7 +239,7 @@
 }
 
 impl<P: binder::BinderAsyncPool> IATest<P> for BpTest {
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())),
@@ -245,7 +247,7 @@
         )
     }
 
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())),
@@ -253,7 +255,7 @@
         )
     }
 
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())),
@@ -261,7 +263,7 @@
         )
     }
 
-    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
         let binder = self.binder.clone();
         P::spawn(
             move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())),
@@ -271,40 +273,40 @@
 }
 
 impl ITest for Binder<BnTest> {
-    fn test(&self) -> binder::Result<String> {
+    fn test(&self) -> Result<String, StatusCode> {
         self.0.test()
     }
 
-    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
+    fn get_dump_args(&self) -> Result<Vec<String>, StatusCode> {
         self.0.get_dump_args()
     }
 
-    fn get_selinux_context(&self) -> binder::Result<String> {
+    fn get_selinux_context(&self) -> Result<String, StatusCode> {
         self.0.get_selinux_context()
     }
 
-    fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+    fn get_is_handling_transaction(&self) -> Result<bool, StatusCode> {
         self.0.get_is_handling_transaction()
     }
 }
 
 impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> {
-    fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn test(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let res = self.0.test();
         Box::pin(async move { res })
     }
 
-    fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+    fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> {
         let res = self.0.get_dump_args();
         Box::pin(async move { res })
     }
 
-    fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+    fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> {
         let res = self.0.get_selinux_context();
         Box::pin(async move { res })
     }
 
-    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+    fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> {
         let res = self.0.get_is_handling_transaction();
         Box::pin(async move { res })
     }
@@ -325,7 +327,7 @@
     _code: TransactionCode,
     _data: &BorrowedParcel<'_>,
     _reply: &mut BorrowedParcel<'_>,
-) -> binder::Result<()> {
+) -> Result<(), StatusCode> {
     Ok(())
 }
 
@@ -363,9 +365,12 @@
     use std::time::Duration;
 
     use binder::{
-        Binder, BinderFeatures, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface,
-        SpIBinder, StatusCode, Strong,
+        BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode,
+        Strong,
     };
+    // Import from impl API for testing only, should not be necessary as long as
+    // you are using AIDL.
+    use binder::binder_impl::{Binder, IBinderInternal, TransactionCode};
 
     use binder_tokio::Tokio;
 
@@ -743,8 +748,7 @@
             let _process = ScopedServiceProcess::new(service_name);
 
             let test_client: Strong<dyn ITest> =
-                binder::get_interface(service_name)
-                .expect("Did not get test binder service");
+                binder::get_interface(service_name).expect("Did not get test binder service");
             let mut remote = test_client.as_binder();
             assert!(remote.is_binder_alive());
             remote.ping_binder().expect("Could not ping remote service");
@@ -925,7 +929,7 @@
         let service2 = service2.as_binder();
 
         let parcel = service1.prepare_transact().unwrap();
-        let res = service2.submit_transact(super::TestTransactionCode::Test as binder::TransactionCode, parcel, 0);
+        let res = service2.submit_transact(super::TestTransactionCode::Test as TransactionCode, parcel, 0);
 
         match res {
             Ok(_) => panic!("submit_transact should fail"),
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 1fc761e..b62da7b 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -18,11 +18,12 @@
 //! access.
 
 use binder::declare_binder_interface;
-use binder::parcel::ParcelFileDescriptor;
 use binder::{
-    Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status,
-    StatusCode, TransactionCode,
+    BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode,
 };
+// Import from impl API for testing only, should not be necessary as long as you
+// are using AIDL.
+use binder::binder_impl::{BorrowedParcel, Binder, TransactionCode};
 
 use std::ffi::{c_void, CStr, CString};
 use std::sync::Once;
@@ -113,7 +114,7 @@
     code: TransactionCode,
     parcel: &BorrowedParcel<'_>,
     reply: &mut BorrowedParcel<'_>,
-) -> Result<()> {
+) -> Result<(), StatusCode> {
     match code {
         bindings::Transaction_TEST_BOOL => {
             assert_eq!(parcel.read::<bool>()?, true);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index ca68b99..c2639e7 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1517,10 +1517,11 @@
     auto keepAlive = sp<BBinder>::make();
     auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive);
 
-    if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+    if (!android::base::GetBoolProperty("ro.debuggable", false) ||
+        android::base::GetProperty("ro.build.type", "") == "user") {
         ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus)
-                << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable builds, "
-                   "but get "
+                << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable or user "
+                   "builds, but get "
                 << statusToString(setRpcClientDebugStatus);
         GTEST_SKIP();
     }
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
index 1fd2c62..73f9d4d 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -14,6 +14,7 @@
         "libbase",
         "libbpf_bcc",
         "libbpf_android",
+        "libbpf_minimal",
         "liblog",
         "libnetdutils"
     ],
@@ -33,6 +34,7 @@
         "libbase",
         "libbpf_bcc",
         "libbpf_android",
+        "libbpf_minimal",
         "libtimeinstate",
         "libnetdutils",
     ],
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 5a80ad0..bc2eb23 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -16,6 +16,7 @@
     srcs: [
         "Flags_test.cpp",
         "cast_test.cpp",
+        "concat_test.cpp",
         "enum_test.cpp",
         "future_test.cpp",
         "small_map_test.cpp",
diff --git a/libs/ftl/concat_test.cpp b/libs/ftl/concat_test.cpp
new file mode 100644
index 0000000..8ecb1b2
--- /dev/null
+++ b/libs/ftl/concat_test.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/concat.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(Concat, Example) {
+  std::string_view name = "Volume";
+  ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB");
+
+  EXPECT_EQ(string.str(), "Vol: -3 dB");
+  EXPECT_EQ(string.c_str()[string.size()], '\0');
+}
+
+namespace {
+
+static_assert(ftl::Concat{"foo"}.str() == "foo");
+static_assert(ftl::Concat{ftl::truncated<3>("foobar")}.str() == "foo");
+
+constexpr ftl::Concat kConcat{"po", "trz", "ebie"};
+
+static_assert(kConcat.size() == 9);
+static_assert(kConcat.max_size() == 9);
+static_assert(kConcat.str() == "potrzebie");
+static_assert(kConcat.str() == std::string_view(kConcat.c_str()));
+
+constexpr auto concat() {
+  return ftl::Concat{ftl::truncated<1>("v???"), ftl::truncated<2>("ee??"),
+                     ftl::truncated<3>("ble?"), ftl::truncated<4>("fetz"),
+                     ftl::truncated<90>("er")};
+}
+
+static_assert(concat().size() == 12);
+static_assert(concat().max_size() == 100);
+static_assert(concat().str() == "veeblefetzer");
+static_assert(concat().str() == std::string_view(concat().c_str()));
+
+}  // namespace
+}  // namespace android::test
diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp
index 1fd43ab..d8ce7a5 100644
--- a/libs/ftl/enum_test.cpp
+++ b/libs/ftl/enum_test.cpp
@@ -72,7 +72,7 @@
   kNeptune
 };
 
-constexpr Planet kPluto{ftl::enum_cast(Planet::kNeptune) + 1};  // Honorable mention.
+constexpr Planet kPluto{ftl::to_underlying(Planet::kNeptune) + 1};  // Honorable mention.
 
 static_assert(ftl::enum_begin_v<Planet> == Planet::kMercury);
 static_assert(ftl::enum_last_v<Planet> == Planet::kNeptune);
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 34faf87..dd96683 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -119,16 +119,11 @@
     if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
 }
 
-void BLASTBufferItemConsumer::setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) {
-    std::scoped_lock lock(mBufferQueueMutex);
-    mBLASTBufferQueue = blastbufferqueue;
-}
-
 void BLASTBufferItemConsumer::onSidebandStreamChanged() {
-    std::scoped_lock lock(mBufferQueueMutex);
-    if (mBLASTBufferQueue != nullptr) {
+    sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+    if (bbq != nullptr) {
         sp<NativeHandle> stream = getSidebandStream();
-        mBLASTBufferQueue->setSidebandStream(stream);
+        bbq->setSidebandStream(stream);
     }
 }
 
@@ -148,7 +143,7 @@
     mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
                                                       GraphicBuffer::USAGE_HW_COMPOSER |
                                                               GraphicBuffer::USAGE_HW_TEXTURE,
-                                                      1, false);
+                                                      1, false, this);
     static int32_t id = 0;
     mName = name + "#" + std::to_string(id);
     auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
@@ -157,7 +152,6 @@
     mBufferItemConsumer->setName(String8(consumerName.c_str()));
     mBufferItemConsumer->setFrameAvailableListener(this);
     mBufferItemConsumer->setBufferFreedListener(this);
-    mBufferItemConsumer->setBlastBufferQueue(this);
 
     ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
     mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
@@ -174,7 +168,6 @@
 }
 
 BLASTBufferQueue::~BLASTBufferQueue() {
-    mBufferItemConsumer->setBlastBufferQueue(nullptr);
     if (mPendingTransactions.empty()) {
         return;
     }
@@ -197,13 +190,15 @@
 
     SurfaceComposerClient::Transaction t;
     const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
+    if (surfaceControlChanged && mSurfaceControl != nullptr) {
+        BQA_LOGD("Updating SurfaceControl without recreating BBQ");
+    }
     bool applyTransaction = false;
 
     // Always update the native object even though they might have the same layer handle, so we can
     // get the updated transform hint from WM.
     mSurfaceControl = surface;
     if (surfaceControlChanged) {
-        BQA_LOGD("Updating SurfaceControl without recreating BBQ");
         t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
                    layer_state_t::eEnableBackpressure);
         applyTransaction = true;
@@ -507,8 +502,7 @@
             std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
                       std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
     sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
-    t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseCallbackId,
-                 releaseBufferCallback);
+    t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseBufferCallback);
     t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
     t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
     t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
@@ -622,7 +616,7 @@
             if (bufferData) {
                 BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
                          bufferData->frameNumber);
-                releaseBuffer(bufferData->releaseCallbackId, bufferData->acquireFence);
+                releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence);
                 // Because we just released a buffer, we know there's no need to wait for a free
                 // buffer.
                 mayNeedToWaitForBuffer = false;
@@ -1025,7 +1019,6 @@
         mBufferItemConsumer->abandon();
         mBufferItemConsumer->setFrameAvailableListener(nullptr);
         mBufferItemConsumer->setBufferFreedListener(nullptr);
-        mBufferItemConsumer->setBlastBufferQueue(nullptr);
     }
     mBufferItemConsumer = nullptr;
     mConsumer = nullptr;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 7f73013..b7594df 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -428,6 +428,94 @@
         return static_cast<status_t>(reply.readInt32());
     }
 
+    // TODO(b/213909104) : Add unit tests to verify surface flinger boot time APIs
+    status_t getBootDisplayModeSupport(bool* outSupport) const override {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            ALOGE("getBootDisplayModeSupport: failed to write interface token: %d", error);
+            return error;
+        }
+        error = remote()->transact(BnSurfaceComposer::GET_BOOT_DISPLAY_MODE_SUPPORT, data, &reply);
+        if (error != NO_ERROR) {
+            ALOGE("getBootDisplayModeSupport: failed to transact: %d", error);
+            return error;
+        }
+        bool support;
+        error = reply.readBool(&support);
+        if (error != NO_ERROR) {
+            ALOGE("getBootDisplayModeSupport: failed to read support: %d", error);
+            return error;
+        }
+        *outSupport = support;
+        return NO_ERROR;
+    }
+
+    status_t setBootDisplayMode(const sp<IBinder>& display,
+                                ui::DisplayModeId displayModeId) override {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setBootDisplayMode failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("setBootDisplayMode failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = data.writeInt32(displayModeId);
+        if (result != NO_ERROR) {
+            ALOGE("setBootDisplayMode failed to writeIint32: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::SET_BOOT_DISPLAY_MODE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setBootDisplayMode failed to transact: %d", result);
+        }
+        return result;
+    }
+
+    status_t clearBootDisplayMode(const sp<IBinder>& display) override {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("clearBootDisplayMode failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("clearBootDisplayMode failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::CLEAR_BOOT_DISPLAY_MODE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("clearBootDisplayMode failed to transact: %d", result);
+        }
+        return result;
+    }
+
+    status_t getPreferredBootDisplayMode(const sp<IBinder>& display,
+                                         ui::DisplayModeId* displayModeId) override {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getPreferredBootDisplayMode failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getPreferredBootDisplayMode failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_PREFERRED_BOOT_DISPLAY_MODE, data,
+                                    &reply);
+        if (result == NO_ERROR) {
+            reply.writeInt32(*displayModeId);
+        }
+        return result;
+    }
+
     void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1134,6 +1222,34 @@
         return NO_ERROR;
     }
 
+    status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+                                         bool* outSupport) const override {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            ALOGE("getDisplayDecorationSupport: failed to write interface token: %d", error);
+            return error;
+        }
+        error = data.writeStrongBinder(displayToken);
+        if (error != NO_ERROR) {
+            ALOGE("getDisplayDecorationSupport: failed to write display token: %d", error);
+            return error;
+        }
+        error = remote()->transact(BnSurfaceComposer::GET_DISPLAY_DECORATION_SUPPORT, data, &reply);
+        if (error != NO_ERROR) {
+            ALOGE("getDisplayDecorationSupport: failed to transact: %d", error);
+            return error;
+        }
+        bool support;
+        error = reply.readBool(&support);
+        if (error != NO_ERROR) {
+            ALOGE("getDisplayDecorationSupport: failed to read support: %d", error);
+            return error;
+        }
+        *outSupport = support;
+        return NO_ERROR;
+    }
+
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                           int8_t compatibility, int8_t changeFrameRateStrategy) override {
         Parcel data, reply;
@@ -1230,6 +1346,21 @@
         SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener));
         return remote()->transact(BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, data, &reply);
     }
+
+    status_t setOverrideFrameRate(uid_t uid, float frameRate) override {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeUint32, uid);
+        SAFE_PARCEL(data.writeFloat, frameRate);
+
+        status_t err = remote()->transact(BnSurfaceComposer::SET_OVERRIDE_FRAME_RATE, data, &reply);
+        if (err != NO_ERROR) {
+            ALOGE("setOverrideFrameRate: failed to transact %s (%d)", strerror(-err), err);
+            return err;
+        }
+
+        return NO_ERROR;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1493,6 +1624,56 @@
             result = reply->writeInt32(result);
             return result;
         }
+        case GET_BOOT_DISPLAY_MODE_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            bool support = false;
+            status_t result = getBootDisplayModeSupport(&support);
+            if (result == NO_ERROR) {
+                reply->writeBool(support);
+            }
+            return result;
+        }
+        case SET_BOOT_DISPLAY_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("setBootDisplayMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            ui::DisplayModeId displayModeId;
+            result = data.readInt32(&displayModeId);
+            if (result != NO_ERROR) {
+                ALOGE("setBootDisplayMode failed to readInt32: %d", result);
+                return result;
+            }
+            return setBootDisplayMode(display, displayModeId);
+        }
+        case CLEAR_BOOT_DISPLAY_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("clearBootDisplayMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            return clearBootDisplayMode(display);
+        }
+        case GET_PREFERRED_BOOT_DISPLAY_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getPreferredBootDisplayMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            ui::DisplayModeId displayModeId;
+            result = getPreferredBootDisplayMode(display, &displayModeId);
+            if (result == NO_ERROR) {
+                reply->writeInt32(displayModeId);
+            }
+            return result;
+        }
         case SET_AUTO_LOW_LATENCY_MODE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = nullptr;
@@ -2016,6 +2197,19 @@
             return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
                                            lightRadius);
         }
+        case GET_DISPLAY_DECORATION_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> displayToken;
+            status_t error = data.readNullableStrongBinder(&displayToken);
+            if (error != NO_ERROR) {
+                ALOGE("getDisplayDecorationSupport: failed to read display token: %d", error);
+                return error;
+            }
+            bool support = false;
+            error = getDisplayDecorationSupport(displayToken, &support);
+            reply->writeBool(support);
+            return error;
+        }
         case SET_FRAME_RATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> binder;
@@ -2127,6 +2321,17 @@
 
             return removeWindowInfosListener(listener);
         }
+        case SET_OVERRIDE_FRAME_RATE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+            uid_t uid;
+            SAFE_PARCEL(data.readUint32, &uid);
+
+            float frameRate;
+            SAFE_PARCEL(data.readFloat, &frameRate);
+
+            return setOverrideFrameRate(uid, frameRate);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index ec0573a..eec4a87 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -153,7 +153,8 @@
     SAFE_PARCEL(output.writeBool, isTrustedOverlay);
 
     SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode));
-    SAFE_PARCEL(bufferData.write, output);
+    SAFE_PARCEL(output.writeNullableParcelable,
+                bufferData ? std::make_optional<BufferData>(*bufferData) : std::nullopt);
     return NO_ERROR;
 }
 
@@ -263,7 +264,9 @@
     uint32_t mode;
     SAFE_PARCEL(input.readUint32, &mode);
     dropInputMode = static_cast<gui::DropInputMode>(mode);
-    SAFE_PARCEL(bufferData.read, input);
+    std::optional<BufferData> tmpBufferData;
+    SAFE_PARCEL(input.readParcelable, &tmpBufferData);
+    bufferData = tmpBufferData ? std::make_shared<BufferData>(*tmpBufferData) : nullptr;
     return NO_ERROR;
 }
 
@@ -337,6 +340,79 @@
     }
 }
 
+void layer_state_t::sanitize(int32_t permissions) {
+    // TODO: b/109894387
+    //
+    // SurfaceFlinger's renderer is not prepared to handle cropping in the face of arbitrary
+    // rotation. To see the problem observe that if we have a square parent, and a child
+    // of the same size, then we rotate the child 45 degrees around its center, the child
+    // must now be cropped to a non rectangular 8 sided region.
+    //
+    // Of course we can fix this in the future. For now, we are lucky, SurfaceControl is
+    // private API, and arbitrary rotation is used in limited use cases, for instance:
+    // - WindowManager only uses rotation in one case, which is on a top level layer in which
+    //   cropping is not an issue.
+    // - Launcher, as a privileged app, uses this to transition an application to PiP
+    //   (picture-in-picture) mode.
+    //
+    // However given that abuse of rotation matrices could lead to surfaces extending outside
+    // of cropped areas, we need to prevent non-root clients without permission
+    // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER
+    // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle
+    // preserving transformations.
+    if (what & eMatrixChanged) {
+        if (!(permissions & Permission::ROTATE_SURFACE_FLINGER)) {
+            ui::Transform t;
+            t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+            if (!t.preserveRects()) {
+                what &= ~eMatrixChanged;
+                ALOGE("Stripped non rect preserving matrix in sanitize");
+            }
+        }
+    }
+
+    if (what & eFlagsChanged) {
+        if ((flags & eLayerIsDisplayDecoration) &&
+            !(permissions & Permission::INTERNAL_SYSTEM_WINDOW)) {
+            flags &= ~eLayerIsDisplayDecoration;
+            ALOGE("Stripped attempt to set LayerIsDisplayDecoration in sanitize");
+        }
+    }
+
+    if (what & layer_state_t::eInputInfoChanged) {
+        if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~eInputInfoChanged;
+            ALOGE("Stripped attempt to set eInputInfoChanged in sanitize");
+        }
+    }
+    if (what & layer_state_t::eTrustedOverlayChanged) {
+        if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~eTrustedOverlayChanged;
+            ALOGE("Stripped attempt to set eTrustedOverlay in sanitize");
+        }
+    }
+    if (what & layer_state_t::eDropInputModeChanged) {
+        if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~eDropInputModeChanged;
+            ALOGE("Stripped attempt to set eDropInputModeChanged in sanitize");
+        }
+    }
+    if (what & layer_state_t::eFrameRateSelectionPriority) {
+        if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~eFrameRateSelectionPriority;
+            ALOGE("Stripped attempt to set eFrameRateSelectionPriority in sanitize");
+        }
+    }
+    if (what & layer_state_t::eFrameRateChanged) {
+        if (!ValidateFrameRate(frameRate, frameRateCompatibility,
+                               changeFrameRateStrategy,
+                               "layer_state_t::sanitize",
+                               permissions & Permission::ACCESS_SURFACE_FLINGER)) {
+            what &= ~eFrameRateChanged; // logged in ValidateFrameRate
+        }
+    }
+}
+
 void layer_state_t::merge(const layer_state_t& other) {
     if (other.what & ePositionChanged) {
         what |= ePositionChanged;
@@ -518,7 +594,7 @@
 }
 
 bool layer_state_t::hasValidBuffer() const {
-    return bufferData.buffer || bufferData.cachedBuffer.isValid();
+    return bufferData && (bufferData->buffer || bufferData->cachedBuffer.isValid());
 }
 
 status_t layer_state_t::matrix22_t::write(Parcel& output) const {
@@ -677,64 +753,68 @@
     return NO_ERROR;
 }
 
-status_t BufferData::write(Parcel& output) const {
-    SAFE_PARCEL(output.writeInt32, flags.get());
+ReleaseCallbackId BufferData::generateReleaseCallbackId() const {
+    return {buffer->getId(), frameNumber};
+}
+
+status_t BufferData::writeToParcel(Parcel* output) const {
+    SAFE_PARCEL(output->writeInt32, flags.get());
 
     if (buffer) {
-        SAFE_PARCEL(output.writeBool, true);
-        SAFE_PARCEL(output.write, *buffer);
+        SAFE_PARCEL(output->writeBool, true);
+        SAFE_PARCEL(output->write, *buffer);
     } else {
-        SAFE_PARCEL(output.writeBool, false);
+        SAFE_PARCEL(output->writeBool, false);
     }
 
     if (acquireFence) {
-        SAFE_PARCEL(output.writeBool, true);
-        SAFE_PARCEL(output.write, *acquireFence);
+        SAFE_PARCEL(output->writeBool, true);
+        SAFE_PARCEL(output->write, *acquireFence);
     } else {
-        SAFE_PARCEL(output.writeBool, false);
+        SAFE_PARCEL(output->writeBool, false);
     }
 
-    SAFE_PARCEL(output.writeUint64, frameNumber);
-    SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
-    SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint);
+    SAFE_PARCEL(output->writeUint64, frameNumber);
+    SAFE_PARCEL(output->writeStrongBinder, IInterface::asBinder(releaseBufferListener));
+    SAFE_PARCEL(output->writeStrongBinder, releaseBufferEndpoint);
 
-    SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
-    SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
+    SAFE_PARCEL(output->writeStrongBinder, cachedBuffer.token.promote());
+    SAFE_PARCEL(output->writeUint64, cachedBuffer.id);
 
     return NO_ERROR;
 }
 
-status_t BufferData::read(const Parcel& input) {
+status_t BufferData::readFromParcel(const Parcel* input) {
     int32_t tmpInt32;
-    SAFE_PARCEL(input.readInt32, &tmpInt32);
+    SAFE_PARCEL(input->readInt32, &tmpInt32);
     flags = Flags<BufferDataChange>(tmpInt32);
 
     bool tmpBool = false;
-    SAFE_PARCEL(input.readBool, &tmpBool);
+    SAFE_PARCEL(input->readBool, &tmpBool);
     if (tmpBool) {
         buffer = new GraphicBuffer();
-        SAFE_PARCEL(input.read, *buffer);
+        SAFE_PARCEL(input->read, *buffer);
     }
 
-    SAFE_PARCEL(input.readBool, &tmpBool);
+    SAFE_PARCEL(input->readBool, &tmpBool);
     if (tmpBool) {
         acquireFence = new Fence();
-        SAFE_PARCEL(input.read, *acquireFence);
+        SAFE_PARCEL(input->read, *acquireFence);
     }
 
-    SAFE_PARCEL(input.readUint64, &frameNumber);
+    SAFE_PARCEL(input->readUint64, &frameNumber);
 
     sp<IBinder> tmpBinder = nullptr;
-    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    SAFE_PARCEL(input->readNullableStrongBinder, &tmpBinder);
     if (tmpBinder) {
         releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
     }
-    SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint);
+    SAFE_PARCEL(input->readNullableStrongBinder, &releaseBufferEndpoint);
 
     tmpBinder = nullptr;
-    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    SAFE_PARCEL(input->readNullableStrongBinder, &tmpBinder);
     cachedBuffer.token = tmpBinder;
-    SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
+    SAFE_PARCEL(input->readUint64, &cachedBuffer.id);
 
     return NO_ERROR;
 }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index cf04ec8..a7b2bcd 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -565,6 +565,13 @@
     mListenerCallbacks = other.mListenerCallbacks;
 }
 
+void SurfaceComposerClient::Transaction::sanitize() {
+    for (auto & [handle, composerState] : mComposerStates) {
+        composerState.state.sanitize(0 /* permissionMask */);
+    }
+    mInputWindowCommands.clear();
+}
+
 std::unique_ptr<SurfaceComposerClient::Transaction>
 SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) {
     auto transaction = std::make_unique<Transaction>();
@@ -646,7 +653,6 @@
         if (composerState.read(*parcel) == BAD_VALUE) {
             return BAD_VALUE;
         }
-
         composerStates[surfaceControlHandle] = composerState;
     }
 
@@ -728,18 +734,18 @@
         return;
     }
 
-    auto listener = state.bufferData.releaseBufferListener;
+    auto listener = state.bufferData->releaseBufferListener;
     sp<Fence> fence =
-            state.bufferData.acquireFence ? state.bufferData.acquireFence : Fence::NO_FENCE;
-    if (state.bufferData.releaseBufferEndpoint ==
+            state.bufferData->acquireFence ? state.bufferData->acquireFence : Fence::NO_FENCE;
+    if (state.bufferData->releaseBufferEndpoint ==
         IInterface::asBinder(TransactionCompletedListener::getIInstance())) {
         // if the callback is in process, run on a different thread to avoid any lock contigency
         // issues in the client.
         SurfaceComposerClient::getDefault()
-                ->mReleaseCallbackThread.addReleaseCallback(state.bufferData.releaseCallbackId,
-                                                            fence);
+                ->mReleaseCallbackThread
+                .addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence);
     } else {
-        listener->onReleaseBuffer(state.bufferData.releaseCallbackId, fence, UINT_MAX);
+        listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX);
     }
 }
 
@@ -839,7 +845,8 @@
         layer_state_t* s = &(mComposerStates[handle].state);
         if (!(s->what & layer_state_t::eBufferChanged)) {
             continue;
-        } else if (s->bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged)) {
+        } else if (s->bufferData &&
+                   s->bufferData->flags.test(BufferData::BufferDataChange::cachedBufferChanged)) {
             // If eBufferChanged and eCachedBufferChanged are both trued then that means
             // we already cached the buffer in a previous call to cacheBuffers, perhaps
             // from writeToParcel on a Transaction that was merged in to this one.
@@ -848,22 +855,22 @@
 
         // Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste
         // time trying to cache them.
-        if (!s->bufferData.buffer) {
+        if (!s->bufferData || !s->bufferData->buffer) {
             continue;
         }
 
         uint64_t cacheId = 0;
-        status_t ret = BufferCache::getInstance().getCacheId(s->bufferData.buffer, &cacheId);
+        status_t ret = BufferCache::getInstance().getCacheId(s->bufferData->buffer, &cacheId);
         if (ret == NO_ERROR) {
             // Cache-hit. Strip the buffer and send only the id.
-            s->bufferData.buffer = nullptr;
+            s->bufferData->buffer = nullptr;
         } else {
             // Cache-miss. Include the buffer and send the new cacheId.
-            cacheId = BufferCache::getInstance().cache(s->bufferData.buffer);
+            cacheId = BufferCache::getInstance().cache(s->bufferData->buffer);
         }
-        s->bufferData.flags |= BufferData::BufferDataChange::cachedBufferChanged;
-        s->bufferData.cachedBuffer.token = BufferCache::getInstance().getToken();
-        s->bufferData.cachedBuffer.id = cacheId;
+        s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
+        s->bufferData->cachedBuffer.token = BufferCache::getInstance().getToken();
+        s->bufferData->cachedBuffer.id = cacheId;
 
         // If we have more buffers than the size of the cache, we should stop caching so we don't
         // evict other buffers in this transaction
@@ -1322,23 +1329,22 @@
     return *this;
 }
 
-std::optional<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffer(
+std::shared_ptr<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffer(
         const sp<SurfaceControl>& sc) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
-        return std::nullopt;
+        return nullptr;
     }
     if (!(s->what & layer_state_t::eBufferChanged)) {
-        return std::nullopt;
+        return nullptr;
     }
 
-    BufferData bufferData = s->bufferData;
+    std::shared_ptr<BufferData> bufferData = std::move(s->bufferData);
 
     TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(
-            bufferData.releaseCallbackId);
-    BufferData emptyBufferData;
+            bufferData->generateReleaseCallbackId());
     s->what &= ~layer_state_t::eBufferChanged;
-    s->bufferData = emptyBufferData;
+    s->bufferData = nullptr;
 
     mContainsBuffer = false;
     return bufferData;
@@ -1347,7 +1353,7 @@
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
         const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
         const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
-        const ReleaseCallbackId& id, ReleaseBufferCallback callback) {
+        ReleaseBufferCallback callback) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -1356,24 +1362,24 @@
 
     releaseBufferIfOverwriting(*s);
 
-    BufferData bufferData;
-    bufferData.buffer = buffer;
+    std::shared_ptr<BufferData> bufferData = std::make_shared<BufferData>();
+    bufferData->buffer = buffer;
     if (frameNumber) {
-        bufferData.frameNumber = *frameNumber;
-        bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+        bufferData->frameNumber = *frameNumber;
+        bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
     }
     if (fence) {
-        bufferData.acquireFence = *fence;
-        bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+        bufferData->acquireFence = *fence;
+        bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
     }
-    bufferData.releaseBufferEndpoint =
+    bufferData->releaseBufferEndpoint =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     if (mIsAutoTimestamp) {
         mDesiredPresentTime = systemTime();
     }
-    setReleaseBufferCallback(&bufferData, id, callback);
+    setReleaseBufferCallback(bufferData.get(), callback);
     s->what |= layer_state_t::eBufferChanged;
-    s->bufferData = bufferData;
+    s->bufferData = std::move(bufferData);
     registerSurfaceControlForCallback(sc);
 
     mContainsBuffer = true;
@@ -1381,7 +1387,6 @@
 }
 
 void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData,
-                                                                  const ReleaseCallbackId& id,
                                                                   ReleaseBufferCallback callback) {
     if (!callback) {
         return;
@@ -1394,9 +1399,8 @@
     }
 
     bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance();
-    bufferData->releaseCallbackId = id;
     auto listener = TransactionCompletedListener::getInstance();
-    listener->setReleaseBufferCallback(id, callback);
+    listener->setReleaseBufferCallback(bufferData->generateReleaseCallbackId(), callback);
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace(
@@ -2077,6 +2081,29 @@
     return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
 }
 
+status_t SurfaceComposerClient::getBootDisplayModeSupport(bool* support) {
+    return ComposerService::getComposerService()->getBootDisplayModeSupport(support);
+}
+
+status_t SurfaceComposerClient::setBootDisplayMode(const sp<IBinder>& display,
+                                                   ui::DisplayModeId displayModeId) {
+    return ComposerService::getComposerService()->setBootDisplayMode(display, displayModeId);
+}
+
+status_t SurfaceComposerClient::clearBootDisplayMode(const sp<IBinder>& display) {
+    return ComposerService::getComposerService()->clearBootDisplayMode(display);
+}
+
+status_t SurfaceComposerClient::getPreferredBootDisplayMode(const sp<IBinder>& display,
+                                                            ui::DisplayModeId* displayModeId) {
+    return ComposerService::getComposerService()->getPreferredBootDisplayMode(display,
+                                                                              displayModeId);
+}
+
+status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) {
+    return ComposerService::getComposerService()->setOverrideFrameRate(uid, frameRate);
+}
+
 void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
     ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
 }
@@ -2218,6 +2245,12 @@
                                                                           lightRadius);
 }
 
+bool SurfaceComposerClient::getDisplayDecorationSupport(const sp<IBinder>& displayToken) {
+    bool support = false;
+    ComposerService::getComposerService()->getDisplayDecorationSupport(displayToken, &support);
+    return support;
+}
+
 int SurfaceComposerClient::getGPUContextPriority() {
     return ComposerService::getComposerService()->getGPUContextPriority();
 }
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 8b2310d..f77cfe6 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -38,11 +38,11 @@
 class BLASTBufferItemConsumer : public BufferItemConsumer {
 public:
     BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
-                            int bufferCount, bool controlledByApp)
+                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
           : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+            mBLASTBufferQueue(std::move(bbq)),
             mCurrentlyConnected(false),
-            mPreviouslyConnected(false),
-            mBLASTBufferQueue(nullptr) {}
+            mPreviouslyConnected(false) {}
 
     void onDisconnect() override;
     void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -53,21 +53,20 @@
                                CompositorTiming compositorTiming, nsecs_t latchTime,
                                nsecs_t dequeueReadyTime) REQUIRES(mMutex);
     void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect);
-    void setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) REQUIRES(mMutex);
 
 protected:
     void onSidebandStreamChanged() override REQUIRES(mMutex);
 
 private:
+    const wp<BLASTBufferQueue> mBLASTBufferQueue;
+
     uint64_t mCurrentFrameNumber = 0;
 
     Mutex mMutex;
-    std::mutex mBufferQueueMutex;
     ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mMutex);
     std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mMutex);
     bool mCurrentlyConnected GUARDED_BY(mMutex);
     bool mPreviouslyConnected GUARDED_BY(mMutex);
-    BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mBufferQueueMutex);
 };
 
 class BLASTBufferQueue
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 2546e4c..fb4fb7e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -224,6 +224,35 @@
             ui::ColorMode colorMode) = 0;
 
     /**
+     * Sets the user-preferred display mode that a device should boot in.
+     */
+    virtual status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId) = 0;
+
+    /**
+     * Clears the user-preferred display mode. The device should now boot in system preferred
+     * display mode.
+     */
+    virtual status_t clearBootDisplayMode(const sp<IBinder>& display) = 0;
+
+    /**
+     * Gets the display mode in which the device boots if there is no user-preferred display mode.
+     */
+    virtual status_t getPreferredBootDisplayMode(const sp<IBinder>& display,
+                                                 ui::DisplayModeId*) = 0;
+
+    /**
+     * Gets whether boot time display mode operations are supported on the device.
+     *
+     * outSupport
+     *      An output parameter for whether boot time display mode operations are supported.
+     *
+     * 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 getBootDisplayModeSupport(bool* outSupport) const = 0;
+
+    /**
      * Switches Auto Low Latency Mode on/off on the connected display, if it is
      * available. This should only be called if the display supports Auto Low
      * Latency Mode as reported in #getDynamicDisplayInfo.
@@ -508,12 +537,35 @@
                                              float lightRadius) = 0;
 
     /*
+     * Gets whether a display supports DISPLAY_DECORATION layers.
+     *
+     * displayToken
+     *      The token of the display.
+     * outSupport
+     *      An output parameter for whether the display supports
+     *      DISPLAY_DECORATION layers.
+     *
+     * Returns NO_ERROR upon success. Otherwise,
+     *      NAME_NOT_FOUND if the display is invalid, or
+     *      BAD_VALUE      if the output parameter is invalid.
+     */
+    virtual status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+                                                 bool* outSupport) const = 0;
+
+    /*
      * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
      */
     virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                                   int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
 
     /*
+     * Set the override frame rate for a specified uid by GameManagerService.
+     * Passing the frame rate and uid to SurfaceFlinger to update the override mapping
+     * in the scheduler.
+     */
+    virtual status_t setOverrideFrameRate(uid_t uid, float frameRate) = 0;
+
+    /*
      * Sets the frame timeline vsync info received from choreographer that corresponds to next
      * buffer submitted on that surface.
      */
@@ -628,6 +680,12 @@
         ADD_WINDOW_INFOS_LISTENER,
         REMOVE_WINDOW_INFOS_LISTENER,
         GET_PRIMARY_PHYSICAL_DISPLAY_ID,
+        GET_DISPLAY_DECORATION_SUPPORT,
+        GET_BOOT_DISPLAY_MODE_SUPPORT,
+        SET_BOOT_DISPLAY_MODE,
+        CLEAR_BOOT_DISPLAY_MODE,
+        GET_PREFERRED_BOOT_DISPLAY_MODE,
+        SET_OVERRIDE_FRAME_RATE,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index a0d3162..cd6afd2 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -58,7 +58,22 @@
     bool isValid() const { return token != nullptr; }
 };
 
-struct BufferData {
+class BufferData : public Parcelable {
+public:
+    virtual ~BufferData() = default;
+    virtual bool hasBuffer() const { return buffer != nullptr; }
+    virtual bool hasSameBuffer(const BufferData& other) const {
+        return buffer == other.buffer && frameNumber == other.frameNumber;
+    }
+    virtual uint32_t getWidth() const { return buffer->getWidth(); }
+    virtual uint32_t getHeight() const { return buffer->getHeight(); }
+    Rect getBounds() const {
+        return {0, 0, static_cast<int32_t>(getWidth()), static_cast<int32_t>(getHeight())};
+    }
+    virtual uint64_t getId() const { return buffer->getId(); }
+    virtual PixelFormat getPixelFormat() const { return buffer->getPixelFormat(); }
+    virtual uint64_t getUsage() const { return buffer->getUsage(); }
+
     enum class BufferDataChange : uint32_t {
         fenceChanged = 0x01,
         frameNumberChanged = 0x02,
@@ -77,12 +92,6 @@
     // buffer id to identify the buffer.
     sp<ITransactionCompletedListener> releaseBufferListener = nullptr;
 
-    // Keeps track of the release callback id associated with the listener. This
-    // is not sent to the server since the id can be reconstructed there. This
-    // is used to remove the old callback from the client process map if it is
-    // overwritten by another setBuffer call.
-    ReleaseCallbackId releaseCallbackId = ReleaseCallbackId::INVALID_ID;
-
     // Stores which endpoint the release information should be sent to. We don't want to send the
     // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
     // was called with.
@@ -92,14 +101,24 @@
 
     client_cache_t cachedBuffer;
 
-    status_t write(Parcel& output) const;
-    status_t read(const Parcel& input);
+    // Generates the release callback id based on the buffer id and frame number.
+    // This is used as an identifier when release callbacks are invoked.
+    ReleaseCallbackId generateReleaseCallbackId() const;
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
 };
 
 /*
  * Used to communicate layer information between SurfaceFlinger and its clients.
  */
 struct layer_state_t {
+    enum Permission {
+        ACCESS_SURFACE_FLINGER = 0x1,
+        ROTATE_SURFACE_FLINGER = 0x2,
+        INTERNAL_SYSTEM_WINDOW = 0x4,
+    };
+
     enum {
         eLayerHidden = 0x01,         // SURFACE_HIDDEN in SurfaceControl.java
         eLayerOpaque = 0x02,         // SURFACE_OPAQUE
@@ -123,7 +142,7 @@
         eLayerStackChanged = 0x00000080,
         /* unused 0x00000400, */
         eShadowRadiusChanged = 0x00000800,
-        eLayerCreated = 0x00001000,
+        /* unused 0x00001000, */
         eBufferCropChanged = 0x00002000,
         eRelativeLayerChanged = 0x00004000,
         eReparent = 0x00008000,
@@ -168,6 +187,7 @@
     status_t read(const Parcel& input);
     bool hasBufferChanges() const;
     bool hasValidBuffer() const;
+    void sanitize(int32_t permissions);
 
     struct matrix22_t {
         float dsdx{0};
@@ -207,7 +227,7 @@
     uint32_t transform;
     bool transformToDisplayInverse;
     Rect crop;
-    BufferData bufferData;
+    std::shared_ptr<BufferData> bufferData = nullptr;
     ui::Dataspace dataspace;
     HdrMetadata hdrMetadata;
     Region surfaceDamageRegion;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 17b4846..366577d 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -25,6 +25,7 @@
 
 #include <binder/IBinder.h>
 
+#include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
 #include <utils/SortedVector.h>
@@ -167,6 +168,18 @@
     static status_t setActiveColorMode(const sp<IBinder>& display,
             ui::ColorMode colorMode);
 
+    // Gets if boot display mode operations are supported on a device
+    static status_t getBootDisplayModeSupport(bool* support);
+    // Sets the user-preferred display mode that a device should boot in
+    static status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId);
+    // Clears the user-preferred display mode
+    static status_t clearBootDisplayMode(const sp<IBinder>& display);
+    // Gets the display mode in which the device boots if there is no user-preferred display mode
+    static status_t getPreferredBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId*);
+    // Sets the frame rate of a particular app (uid). This is currently called
+    // by GameManager.
+    static status_t setOverrideFrameRate(uid_t uid, float frameRate);
+
     // Switches on/off Auto Low Latency Mode on the connected display. This should only be
     // called if the connected display supports Auto Low Latency Mode as reported by
     // #getAutoLowLatencyModeSupport
@@ -272,6 +285,16 @@
     static status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                             float lightPosY, float lightPosZ, float lightRadius);
 
+    /*
+     * Returns whether a display supports DISPLAY_DECORATION layers.
+     *
+     * displayToken
+     *      The token of the display.
+     *
+     * Returns whether a display supports DISPLAY_DECORATION layers.
+     */
+    static bool getDisplayDecorationSupport(const sp<IBinder>& displayToken);
+
     // ------------------------------------------------------------------------
     // surface creation / destruction
 
@@ -416,7 +439,7 @@
 
         void cacheBuffers();
         void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
-        void setReleaseBufferCallback(BufferData*, const ReleaseCallbackId&, ReleaseBufferCallback);
+        void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback);
 
     public:
         Transaction();
@@ -490,9 +513,8 @@
         Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
                                const std::optional<sp<Fence>>& fence = std::nullopt,
                                const std::optional<uint64_t>& frameNumber = std::nullopt,
-                               const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID,
                                ReleaseBufferCallback callback = nullptr);
-        std::optional<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
+        std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
         Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
         Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
@@ -602,6 +624,14 @@
         void setAnimationTransaction();
         void setEarlyWakeupStart();
         void setEarlyWakeupEnd();
+
+        /**
+         * Strip the transaction of all permissioned requests, required when
+         * accepting transactions across process boundaries.
+         *
+         * TODO (b/213644870): Remove all permissioned things from Transaction
+         */
+        void sanitize();
     };
 
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index d6ac3f9..0ebd11c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -31,9 +31,11 @@
 #include <gui/SyncScreenCaptureListener.h>
 #include <inttypes.h>
 #include <private/gui/ComposerService.h>
+#include <sys/types.h>
 #include <ui/BufferQueueDefs.h>
 #include <ui/DisplayMode.h>
 #include <ui/Rect.h>
+#include <utils/Errors.h>
 #include <utils/String8.h>
 
 #include <limits>
@@ -755,6 +757,15 @@
     }
     status_t setActiveColorMode(const sp<IBinder>& /*display*/,
         ColorMode /*colorMode*/) override { return NO_ERROR; }
+    status_t getBootDisplayModeSupport(bool* /*outSupport*/) const override { return NO_ERROR; }
+    status_t setBootDisplayMode(const sp<IBinder>& /*display*/, ui::DisplayModeId /*id*/) override {
+        return NO_ERROR;
+    }
+    status_t clearBootDisplayMode(const sp<IBinder>& /*display*/) override { return NO_ERROR; }
+    status_t getPreferredBootDisplayMode(const sp<IBinder>& /*display*/,
+                                         ui::DisplayModeId* /*id*/) override {
+        return NO_ERROR;
+    }
     void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
     void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
 
@@ -882,6 +893,11 @@
         return NO_ERROR;
     }
 
+    status_t getDisplayDecorationSupport(const sp<IBinder>& /*displayToken*/,
+                                         bool* /*outSupport*/) const override {
+        return NO_ERROR;
+    }
+
     status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
                           int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
         return NO_ERROR;
@@ -911,6 +927,8 @@
         return NO_ERROR;
     }
 
+    status_t setOverrideFrameRate(uid_t /*uid*/, float /*frameRate*/) override { return NO_ERROR; }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 84dba84..3073d94 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -456,7 +456,8 @@
     mRawTransform = rawTransform;
     mDownTime = downTime;
     mPointerProperties.clear();
-    mPointerProperties.appendArray(pointerProperties, pointerCount);
+    mPointerProperties.insert(mPointerProperties.end(), &pointerProperties[0],
+                              &pointerProperties[pointerCount]);
     mSampleEventTimes.clear();
     mSamplePointerCoords.clear();
     addSample(eventTime, pointerCoords);
@@ -490,8 +491,10 @@
         mSamplePointerCoords.clear();
         size_t pointerCount = other->getPointerCount();
         size_t historySize = other->getHistorySize();
-        mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array()
-                + (historySize * pointerCount), pointerCount);
+        mSamplePointerCoords
+                .insert(mSamplePointerCoords.end(),
+                        &other->mSamplePointerCoords[historySize * pointerCount],
+                        &other->mSamplePointerCoords[historySize * pointerCount + pointerCount]);
     }
 }
 
@@ -499,7 +502,8 @@
         int64_t eventTime,
         const PointerCoords* pointerCoords) {
     mSampleEventTimes.push_back(eventTime);
-    mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
+    mSamplePointerCoords.insert(mSamplePointerCoords.end(), &pointerCoords[0],
+                                &pointerCoords[getPointerCount()]);
 }
 
 int MotionEvent::getSurfaceRotation() const {
@@ -569,7 +573,7 @@
 ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
     size_t pointerCount = mPointerProperties.size();
     for (size_t i = 0; i < pointerCount; i++) {
-        if (mPointerProperties.itemAt(i).id == pointerId) {
+        if (mPointerProperties[i].id == pointerId) {
             return i;
         }
     }
@@ -591,8 +595,7 @@
 
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
-        mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor,
-                                                 globalScaleFactor);
+        mSamplePointerCoords[i].scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
     }
 }
 
@@ -686,15 +689,15 @@
     mDownTime = parcel->readInt64();
 
     mPointerProperties.clear();
-    mPointerProperties.setCapacity(pointerCount);
+    mPointerProperties.reserve(pointerCount);
     mSampleEventTimes.clear();
     mSampleEventTimes.reserve(sampleCount);
     mSamplePointerCoords.clear();
-    mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
+    mSamplePointerCoords.reserve(sampleCount * pointerCount);
 
     for (size_t i = 0; i < pointerCount; i++) {
-        mPointerProperties.push();
-        PointerProperties& properties = mPointerProperties.editTop();
+        mPointerProperties.push_back({});
+        PointerProperties& properties = mPointerProperties.back();
         properties.id = parcel->readInt32();
         properties.toolType = parcel->readInt32();
     }
@@ -703,8 +706,8 @@
         sampleCount--;
         mSampleEventTimes.push_back(parcel->readInt64());
         for (size_t i = 0; i < pointerCount; i++) {
-            mSamplePointerCoords.push();
-            status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
+            mSamplePointerCoords.push_back({});
+            status_t status = mSamplePointerCoords.back().readFromParcel(parcel);
             if (status) {
                 return status;
             }
@@ -750,12 +753,12 @@
     parcel->writeInt64(mDownTime);
 
     for (size_t i = 0; i < pointerCount; i++) {
-        const PointerProperties& properties = mPointerProperties.itemAt(i);
+        const PointerProperties& properties = mPointerProperties[i];
         parcel->writeInt32(properties.id);
         parcel->writeInt32(properties.toolType);
     }
 
-    const PointerCoords* pc = mSamplePointerCoords.array();
+    const PointerCoords* pc = mSamplePointerCoords.data();
     for (size_t h = 0; h < sampleCount; h++) {
         parcel->writeInt64(mSampleEventTimes[h]);
         for (size_t i = 0; i < pointerCount; i++) {
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 3baeb55..2039fa6 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -86,10 +86,13 @@
 
 // --- KeyCharacterMap ---
 
-KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {}
+KeyCharacterMap::KeyCharacterMap(const std::string& filename)
+      : mType(KeyboardType::UNKNOWN), mLoadFileName(filename) {}
 
 KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
       : mType(other.mType),
+        mLoadFileName(other.mLoadFileName),
+        mLayoutOverlayApplied(other.mLayoutOverlayApplied),
         mKeysByScanCode(other.mKeysByScanCode),
         mKeysByUsageCode(other.mKeysByUsageCode) {
     for (size_t i = 0; i < other.mKeys.size(); i++) {
@@ -98,16 +101,19 @@
 }
 
 KeyCharacterMap::~KeyCharacterMap() {
-    for (size_t i = 0; i < mKeys.size(); i++) {
-        Key* key = mKeys.editValueAt(i);
-        delete key;
-    }
+    clear();
 }
 
 bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const {
     if (mType != other.mType) {
         return false;
     }
+    if (mLoadFileName != other.mLoadFileName) {
+        return false;
+    }
+    if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) {
+        return false;
+    }
     if (mKeys.size() != other.mKeys.size() ||
         mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
         mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
@@ -146,6 +152,10 @@
     return true;
 }
 
+bool KeyCharacterMap::operator!=(const KeyCharacterMap& other) const {
+    return !(*this == other);
+}
+
 base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
                                                                      Format format) {
     Tokenizer* tokenizer;
@@ -153,12 +163,18 @@
     if (status) {
         return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
     }
-    std::unique_ptr<Tokenizer> t(tokenizer);
-    auto ret = load(t.get(), format);
-    if (ret.ok()) {
-        (*ret)->mLoadFileName = filename;
+    std::shared_ptr<KeyCharacterMap> map =
+            std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
+    if (!map.get()) {
+        ALOGE("Error allocating key character map.");
+        return Errorf("Error allocating key character map.");
     }
-    return ret;
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    status = map->load(t.get(), format);
+    if (status == OK) {
+        return map;
+    }
+    return Errorf("Load KeyCharacterMap failed {}.", status);
 }
 
 base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents(
@@ -169,40 +185,67 @@
         ALOGE("Error %d opening key character map.", status);
         return Errorf("Error {} opening key character map.", status);
     }
-    std::unique_ptr<Tokenizer> t(tokenizer);
-    auto ret = load(t.get(), format);
-    if (ret.ok()) {
-        (*ret)->mLoadFileName = filename;
-    }
-    return ret;
-}
-
-base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer,
-                                                                     Format format) {
-    status_t status = OK;
-    std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
+    std::shared_ptr<KeyCharacterMap> map =
+            std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(filename));
     if (!map.get()) {
         ALOGE("Error allocating key character map.");
         return Errorf("Error allocating key character map.");
     }
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    status = map->load(t.get(), format);
+    if (status == OK) {
+        return map;
+    }
+    return Errorf("Load KeyCharacterMap failed {}.", status);
+}
+
+status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format) {
+    status_t status = OK;
 #if DEBUG_PARSER_PERFORMANCE
     nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
 #endif
-    Parser parser(map.get(), tokenizer, format);
+    Parser parser(this, tokenizer, format);
     status = parser.parse();
 #if DEBUG_PARSER_PERFORMANCE
     nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
     ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
           tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
 #endif
-    if (status == OK) {
-        return map;
+    if (status != OK) {
+        ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str());
     }
+    return status;
+}
 
-    return Errorf("Load KeyCharacterMap failed {}.", status);
+void KeyCharacterMap::clear() {
+    mKeysByScanCode.clear();
+    mKeysByUsageCode.clear();
+    for (size_t i = 0; i < mKeys.size(); i++) {
+        Key* key = mKeys.editValueAt(i);
+        delete key;
+    }
+    mKeys.clear();
+    mLayoutOverlayApplied = false;
+    mType = KeyboardType::UNKNOWN;
+}
+
+status_t KeyCharacterMap::reloadBaseFromFile() {
+    clear();
+    Tokenizer* tokenizer;
+    status_t status = Tokenizer::open(String8(mLoadFileName.c_str()), &tokenizer);
+    if (status) {
+        ALOGE("Error %s opening key character map file %s.", statusToString(status).c_str(),
+              mLoadFileName.c_str());
+        return status;
+    }
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    return load(t.get(), KeyCharacterMap::Format::BASE);
 }
 
 void KeyCharacterMap::combine(const KeyCharacterMap& overlay) {
+    if (mLayoutOverlayApplied) {
+        reloadBaseFromFile();
+    }
     for (size_t i = 0; i < overlay.mKeys.size(); i++) {
         int32_t keyCode = overlay.mKeys.keyAt(i);
         Key* key = overlay.mKeys.valueAt(i);
@@ -224,7 +267,7 @@
         mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
                                          overlay.mKeysByUsageCode.valueAt(i));
     }
-    mLoadFileName = overlay.mLoadFileName;
+    mLayoutOverlayApplied = true;
 }
 
 KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const {
@@ -636,8 +679,11 @@
         ALOGE("%s: Null parcel", __func__);
         return nullptr;
     }
-    std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
+    std::string loadFileName = parcel->readCString();
+    std::shared_ptr<KeyCharacterMap> map =
+            std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap(loadFileName));
     map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
+    map->mLayoutOverlayApplied = parcel->readBool();
     size_t numKeys = parcel->readInt32();
     if (parcel->errorCheck()) {
         return nullptr;
@@ -687,6 +733,30 @@
             return nullptr;
         }
     }
+    size_t numKeysByScanCode = parcel->readInt32();
+    if (parcel->errorCheck()) {
+        return nullptr;
+    }
+    for (size_t i = 0; i < numKeysByScanCode; i++) {
+        int32_t key = parcel->readInt32();
+        int32_t value = parcel->readInt32();
+        map->mKeysByScanCode.add(key, value);
+        if (parcel->errorCheck()) {
+            return nullptr;
+        }
+    }
+    size_t numKeysByUsageCode = parcel->readInt32();
+    if (parcel->errorCheck()) {
+        return nullptr;
+    }
+    for (size_t i = 0; i < numKeysByUsageCode; i++) {
+        int32_t key = parcel->readInt32();
+        int32_t value = parcel->readInt32();
+        map->mKeysByUsageCode.add(key, value);
+        if (parcel->errorCheck()) {
+            return nullptr;
+        }
+    }
     return map;
 }
 
@@ -695,7 +765,9 @@
         ALOGE("%s: Null parcel", __func__);
         return;
     }
+    parcel->writeCString(mLoadFileName.c_str());
     parcel->writeInt32(static_cast<int32_t>(mType));
+    parcel->writeBool(mLayoutOverlayApplied);
 
     size_t numKeys = mKeys.size();
     parcel->writeInt32(numKeys);
@@ -715,6 +787,18 @@
         }
         parcel->writeInt32(0);
     }
+    size_t numKeysByScanCode = mKeysByScanCode.size();
+    parcel->writeInt32(numKeysByScanCode);
+    for (size_t i = 0; i < numKeysByScanCode; i++) {
+        parcel->writeInt32(mKeysByScanCode.keyAt(i));
+        parcel->writeInt32(mKeysByScanCode.valueAt(i));
+    }
+    size_t numKeysByUsageCode = mKeysByUsageCode.size();
+    parcel->writeInt32(numKeysByUsageCode);
+    for (size_t i = 0; i < numKeysByUsageCode; i++) {
+        parcel->writeInt32(mKeysByUsageCode.keyAt(i));
+        parcel->writeInt32(mKeysByUsageCode.valueAt(i));
+    }
 }
 #endif // __linux__
 
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 63bf23d..265cbf0 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -25,13 +25,6 @@
     // android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS.
     const int UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
 
-    // Compatibility changes.
-    /**
-      * TODO(b/157929241): remove this before closing the bug. This is needed temporarily
-      * to identify apps that are using this flag.
-      */
-    const long BLOCK_FLAG_SLIPPERY = 157929241;
-
     // Indicate invalid battery capacity
     const int INVALID_BATTERY_CAPACITY = -1;
 
@@ -102,4 +95,7 @@
          */
         INTERCEPTS_STYLUS = 0x00000040,
     }
+
+    /* The default pointer acceleration value. */
+    const int DEFAULT_POINTER_ACCELERATION = 3;
 }
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 18ab1cb..6ffe851 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -37,6 +37,7 @@
         "libui",
         "libutils",
     ],
+    data: ["data/*.kcm"],
     test_suites: ["device-tests"],
 }
 
@@ -59,5 +60,5 @@
         "libbinder",
         "libui",
         "libbase",
-    ]
+    ],
 }
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index f8f2f4e..61e88df 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -20,6 +20,7 @@
 #include <input/InputDevice.h>
 #include <input/KeyLayoutMap.h>
 #include <input/Keyboard.h>
+#include "android-base/file.h"
 
 namespace android {
 
@@ -82,4 +83,53 @@
     ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
 }
 
+TEST_F(InputDeviceKeyMapTest, keyCharacterMapWithOverlayParcelingTest) {
+    Parcel parcel;
+    std::string overlayPath = base::GetExecutableDirectory() + "/data/german.kcm";
+    base::Result<std::shared_ptr<KeyCharacterMap>> overlay =
+            KeyCharacterMap::load(overlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(overlay.ok()) << "Cannot load KeyCharacterMap at " << overlayPath;
+    mKeyMap.keyCharacterMap->combine(*overlay->get());
+    mKeyMap.keyCharacterMap->writeToParcel(&parcel);
+    parcel.setDataPosition(0);
+    std::shared_ptr<KeyCharacterMap> map = KeyCharacterMap::readFromParcel(&parcel);
+    ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
+}
+
+TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) {
+    std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm";
+    std::string englishOverlayPath = base::GetExecutableDirectory() + "/data/english_us.kcm";
+    std::string germanOverlayPath = base::GetExecutableDirectory() + "/data/german.kcm";
+    base::Result<std::shared_ptr<KeyCharacterMap>> frenchOverlay =
+            KeyCharacterMap::load(frenchOverlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(frenchOverlay.ok()) << "Cannot load KeyCharacterMap at " << frenchOverlayPath;
+    base::Result<std::shared_ptr<KeyCharacterMap>> englishOverlay =
+            KeyCharacterMap::load(englishOverlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(englishOverlay.ok()) << "Cannot load KeyCharacterMap at " << englishOverlayPath;
+    base::Result<std::shared_ptr<KeyCharacterMap>> germanOverlay =
+            KeyCharacterMap::load(germanOverlayPath, KeyCharacterMap::Format::OVERLAY);
+    ASSERT_TRUE(germanOverlay.ok()) << "Cannot load KeyCharacterMap at " << germanOverlayPath;
+
+    // Apply the French overlay
+    mKeyMap.keyCharacterMap->combine(*frenchOverlay->get());
+    // Copy the result for later
+    std::shared_ptr<KeyCharacterMap> frenchOverlaidKeyCharacterMap =
+            std::make_shared<KeyCharacterMap>(*mKeyMap.keyCharacterMap);
+
+    // Apply the English overlay
+    mKeyMap.keyCharacterMap->combine(*englishOverlay->get());
+    // Verify that the result is different from the French overlay result
+    ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
+
+    // Apply the German overlay
+    mKeyMap.keyCharacterMap->combine(*germanOverlay->get());
+    // Verify that the result is different from the French overlay result
+    ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
+
+    // Apply the French overlay
+    mKeyMap.keyCharacterMap->combine(*frenchOverlay->get());
+    // Verify that the result is the same like after applying it initially
+    ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
+}
+
 } // namespace android
diff --git a/libs/input/tests/data/english_us.kcm b/libs/input/tests/data/english_us.kcm
new file mode 100644
index 0000000..d0ef027
--- /dev/null
+++ b/libs/input/tests/data/english_us.kcm
@@ -0,0 +1,311 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# English (US) keyboard layout.
+# Unlike the default (generic) keyboard layout, English (US) does not contain any
+# special ALT characters.
+#
+
+type OVERLAY
+
+### ROW 1
+
+key GRAVE {
+    label:                              '`'
+    base:                               '`'
+    shift:                              '~'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '@'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '^'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '&'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '*'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              '('
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              ')'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+}
+
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+}
+
+key LEFT_BRACKET {
+    label:                              '['
+    base:                               '['
+    shift:                              '{'
+}
+
+key RIGHT_BRACKET {
+    label:                              ']'
+    base:                               ']'
+    shift:                              '}'
+}
+
+key BACKSLASH {
+    label:                              '\\'
+    base:                               '\\'
+    shift:                              '|'
+}
+
+### ROW 3
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key SEMICOLON {
+    label:                              ';'
+    base:                               ';'
+    shift:                              ':'
+}
+
+key APOSTROPHE {
+    label:                              '\''
+    base:                               '\''
+    shift:                              '"'
+}
+
+### ROW 4
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              '<'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              '>'
+}
+
+key SLASH {
+    label:                              '/'
+    base:                               '/'
+    shift:                              '?'
+}
\ No newline at end of file
diff --git a/libs/input/tests/data/french.kcm b/libs/input/tests/data/french.kcm
new file mode 100644
index 0000000..db69ea0
--- /dev/null
+++ b/libs/input/tests/data/french.kcm
@@ -0,0 +1,336 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# French keyboard layout, AZERTY style.
+#
+
+type OVERLAY
+
+map key 16 A
+map key 17 Z
+map key 30 Q
+map key 39 M
+map key 44 W
+map key 50 COMMA
+map key 51 SEMICOLON
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+    label:                              '\u00b2'
+    base:                               '\u00b2'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '&'
+    shift:                              '1'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '\u00e9'
+    shift:                              '2'
+    ralt:                               '~'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '"'
+    shift:                              '3'
+    ralt:                               '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '\''
+    shift:                              '4'
+    ralt:                               '{'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '('
+    shift:                              '5'
+    ralt:                               '['
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '-'
+    shift:                              '6'
+    ralt:                               '|'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '\u00e8'
+    shift:                              '7'
+    ralt:                               '`'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '_'
+    shift:                              '8'
+    ralt:                               '\\'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '\u00e7'
+    shift:                              '9'
+    ralt:                               '^'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '\u00e0'
+    shift:                              '0'
+    ralt:                               '@'
+}
+
+key MINUS {
+    label:                              ')'
+    base:                               ')'
+    shift:                              '\u00b0'
+    ralt:                               ']'
+}
+
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+    ralt:                               '}'
+}
+
+### ROW 2
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u20ac'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+}
+
+key LEFT_BRACKET {
+    label:                              '\u02c6'
+    base:                               '\u0302'
+    shift:                              '\u0308'
+}
+
+key RIGHT_BRACKET {
+    label:                              '$'
+    base:                               '$'
+    shift:                              '\u00a3'
+    ralt:                               '\u00a4'
+}
+
+### ROW 3
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key APOSTROPHE {
+    label:                              '\u00f9'
+    base:                               '\u00f9'
+    shift:                              '%'
+}
+
+key BACKSLASH {
+    label:                              '*'
+    base:                               '*'
+    shift:                              '\u00b5'
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '<'
+    base:                               '<'
+    shift:                              '>'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              '?'
+}
+
+key SEMICOLON {
+    label:                              ';'
+    base:                               ';'
+    shift:                              '.'
+}
+
+key PERIOD {
+    label:                              ':'
+    base:                               ':'
+    shift:                              '/'
+}
+
+key SLASH {
+    label:                              '!'
+    base:                               '!'
+    shift:                              '\u00a7'
+}
\ No newline at end of file
diff --git a/libs/input/tests/data/german.kcm b/libs/input/tests/data/german.kcm
new file mode 100644
index 0000000..2fbc5e5
--- /dev/null
+++ b/libs/input/tests/data/german.kcm
@@ -0,0 +1,336 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# German keyboard layout, QWERTZ style.
+#
+
+type OVERLAY
+
+map key 12 SLASH            # � ? \
+map key 21 Z
+map key 44 Y
+map key 53 MINUS            # - _
+map key 86 PLUS             # < > |
+
+map key usage 32 A              # for testing purposes only
+map key usage 67 B              # for testing purposes only
+
+### ROW 1
+
+key GRAVE {
+    label:                              '^'
+    base:                               '^'
+    shift:                              '\u00b0'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '"'
+    ralt:                               '\u00b2'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '\u00a7'
+    ralt:                               '\u00b3'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '&'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '/'
+    ralt:                               '{'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '('
+    ralt:                               '['
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              ')'
+    ralt:                               ']'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              '='
+    ralt:                               '}'
+}
+
+key SLASH {
+    label:                              '\u00df'
+    base:                               '\u00df'
+    shift:                              '?'
+    ralt:                               '\\'
+}
+
+key EQUALS {
+    label:                              '\u00b4'
+    base:                               '\u0301'
+    shift:                              '\u0300'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+    ralt:                               '@'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u20ac'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+}
+
+key LEFT_BRACKET {
+    label:                              '\u00dc'
+    base:                               '\u00fc'
+    shift, capslock:                    '\u00dc'
+}
+
+key RIGHT_BRACKET {
+    label:                              '+'
+    base:                               '+'
+    shift:                              '*'
+    ralt:                               '~'
+}
+
+### ROW 3
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key SEMICOLON {
+    label:                              '\u00d6'
+    base:                               '\u00f6'
+    shift, capslock:                    '\u00d6'
+}
+
+key APOSTROPHE {
+    label:                              '\u00c4'
+    base:                               '\u00e4'
+    shift, capslock:                    '\u00c4'
+}
+
+key BACKSLASH {
+    label:                              '#'
+    base:                               '#'
+    shift:                              '\''
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '<'
+    base:                               '<'
+    shift:                              '>'
+    ralt:                               '|'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+    ralt:                               '\u00b5'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              ';'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              ':'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+}
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index fc9680b..d90ee57 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -552,17 +552,17 @@
         const AChoreographerFrameCallbackData* data) {
     return AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(data);
 }
-int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+AVsyncId AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
         const AChoreographerFrameCallbackData* data, size_t index) {
     return AChoreographerFrameCallbackData_getFrameTimelineVsyncId(data, index);
 }
-int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(data, index);
+    return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(data, index);
 }
-int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_getFrameTimelineDeadline(data, index);
+    return AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(data, index);
 }
 
 int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) {
@@ -644,7 +644,7 @@
                         "Data is only valid in callback");
     return frameCallbackData->preferredFrameTimelineIndex;
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
         const AChoreographerFrameCallbackData* data, size_t index) {
     const ChoreographerFrameCallbackDataImpl* frameCallbackData =
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
@@ -653,7 +653,7 @@
     LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
     return frameCallbackData->frameTimelines[index].id;
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
     const ChoreographerFrameCallbackDataImpl* frameCallbackData =
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
@@ -662,7 +662,7 @@
     LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
     return frameCallbackData->frameTimelines[index].expectedPresentTime;
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
     const ChoreographerFrameCallbackDataImpl* frameCallbackData =
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 6288194..76b85d6 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -50,11 +50,6 @@
     int32_t height{0};
 
     /**
-     * The display density.
-     */
-    float density{0};
-
-    /**
      * The refresh rate of the display configuration, in frames per second.
      */
     float fps{0.0};
@@ -168,8 +163,8 @@
             const ui::DisplayMode& mode = modes[j];
             modesPerDisplay[i].emplace_back(
                     DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(),
-                                      mode.resolution.getHeight(), staticInfo.density,
-                                      mode.refreshRate, mode.sfVsyncOffset, mode.appVsyncOffset});
+                                      mode.resolution.getHeight(), mode.refreshRate,
+                                      mode.sfVsyncOffset, mode.appVsyncOffset});
         }
     }
 
@@ -283,12 +278,6 @@
     return NAME_NOT_FOUND;
 }
 
-float ADisplayConfig_getDensity(ADisplayConfig* config) {
-    CHECK_NOT_NULL(config);
-
-    return reinterpret_cast<DisplayConfigImpl*>(config)->density;
-}
-
 int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
     CHECK_NOT_NULL(config);
 
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 4aa7e69..d650c26 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -65,11 +65,11 @@
         const AChoreographerFrameCallbackData* data);
 size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
         const AChoreographerFrameCallbackData* data);
-int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+AVsyncId AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
         const AChoreographerFrameCallbackData* data, size_t index);
-int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index);
-int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index);
 
 } // namespace android
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
index bd94b55..0f44902 100644
--- a/libs/nativedisplay/include/apex/display.h
+++ b/libs/nativedisplay/include/apex/display.h
@@ -107,11 +107,6 @@
 int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);
 
 /**
- * Queries the density for a given display configuration.
- */
-float ADisplayConfig_getDensity(ADisplayConfig* config);
-
-/**
  * Queries the width in pixels for a given display configuration.
  */
 int32_t ADisplayConfig_getWidth(ADisplayConfig* config);
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 4dbfde8..6579313 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -12,8 +12,8 @@
     AChoreographerFrameCallbackData_getFrameTimelinesLength; # apex # introduced=33
     AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # apex # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # apex # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime; # apex # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineDeadline; # apex # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos; # apex # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos; # apex # introduced=33
     AChoreographer_create; # apex # introduced=30
     AChoreographer_destroy; # apex # introduced=30
     AChoreographer_getFd; # apex # introduced=30
@@ -40,8 +40,8 @@
       android::AChoreographerFrameCallbackData_routeGetFrameTimelinesLength*;
       android::AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex*;
       android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*;
-      android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime*;
-      android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline*;
+      android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos*;
+      android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos*;
       android::AChoreographer_signalRefreshRateCallbacks*;
       android::AChoreographer_getFrameInterval*;
       android::ADisplay_acquirePhysicalDisplays*;
@@ -50,7 +50,6 @@
       android::ADisplay_getDisplayType*;
       android::ADisplay_getPreferredWideColorFormat*;
       android::ADisplay_getCurrentConfig*;
-      android::ADisplayConfig_getDensity*;
       android::ADisplayConfig_getWidth*;
       android::ADisplayConfig_getHeight*;
       android::ADisplayConfig_getFps*;
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
index eabff58..84771c0 100644
--- a/libs/renderengine/ExternalTexture.cpp
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -14,30 +14,32 @@
  * limitations under the License.
  */
 
-#include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <ui/GraphicBuffer.h>
 
 #include "log/log_main.h"
 
-namespace android::renderengine {
+namespace android::renderengine::impl {
 
-ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine,
-                                 uint32_t usage)
+ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer,
+                                 renderengine::RenderEngine& renderEngine, uint32_t usage)
       : mBuffer(buffer), mRenderEngine(renderEngine) {
     LOG_ALWAYS_FATAL_IF(buffer == nullptr,
                         "Attempted to bind a null buffer to an external texture!");
     // GLESRenderEngine has a separate texture cache for output buffers,
-    if (usage == Usage::WRITEABLE &&
-        (mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::GLES ||
-         mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::THREADED)) {
+    if (usage == WRITEABLE &&
+        (mRenderEngine.getRenderEngineType() ==
+                 renderengine::RenderEngine::RenderEngineType::GLES ||
+         mRenderEngine.getRenderEngineType() ==
+                 renderengine::RenderEngine::RenderEngineType::THREADED)) {
         return;
     }
-    mRenderEngine.mapExternalTextureBuffer(mBuffer, usage & Usage::WRITEABLE);
+    mRenderEngine.mapExternalTextureBuffer(mBuffer, usage & WRITEABLE);
 }
 
 ExternalTexture::~ExternalTexture() {
     mRenderEngine.unmapExternalTextureBuffer(mBuffer);
 }
 
-} // namespace android::renderengine
+} // namespace android::renderengine::impl
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 6c8f8e8..ead97cf 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -22,6 +22,7 @@
 #include <renderengine/ExternalTexture.h>
 #include <renderengine/LayerSettings.h>
 #include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
 
 #include <mutex>
 
@@ -115,15 +116,15 @@
                                                        uint32_t height,
                                                        uint64_t extraUsageFlags = 0,
                                                        std::string name = "output") {
-    return std::make_shared<ExternalTexture>(new GraphicBuffer(width, height,
-                                                               HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                                               GRALLOC_USAGE_HW_RENDER |
-                                                                       GRALLOC_USAGE_HW_TEXTURE |
-                                                                       extraUsageFlags,
-                                                               std::move(name)),
-                                             re,
-                                             ExternalTexture::Usage::READABLE |
-                                                     ExternalTexture::Usage::WRITEABLE);
+    return std::make_shared<
+            impl::ExternalTexture>(new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                     GRALLOC_USAGE_HW_RENDER |
+                                                             GRALLOC_USAGE_HW_TEXTURE |
+                                                             extraUsageFlags,
+                                                     std::move(name)),
+                                   re,
+                                   impl::ExternalTexture::Usage::READABLE |
+                                           impl::ExternalTexture::Usage::WRITEABLE);
 }
 
 static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re,
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index b4cab39..2c51ccd 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -43,6 +43,9 @@
     // Maximum luminance pulled from the display's HDR capabilities.
     float maxLuminance = 1.0f;
 
+    // Current luminance of the display
+    float currentLuminanceNits = -1.f;
+
     // Output dataspace that will be populated if wide color gamut is used, or
     // DataSpace::UNKNOWN otherwise.
     ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
diff --git a/libs/renderengine/include/renderengine/ExternalTexture.h b/libs/renderengine/include/renderengine/ExternalTexture.h
index 07f0833..621a209 100644
--- a/libs/renderengine/include/renderengine/ExternalTexture.h
+++ b/libs/renderengine/include/renderengine/ExternalTexture.h
@@ -33,28 +33,22 @@
  */
 class ExternalTexture {
 public:
-    // Usage specifies the rendering intent for the buffer.
-    enum Usage : uint32_t {
-        // When a buffer is not READABLE but is WRITEABLE, then GLESRenderEngine will use that as a
-        // hint to load the buffer into a separate cache
-        READABLE = 1 << 0,
+    ExternalTexture() = default;
+    virtual ~ExternalTexture() = default;
 
-        // The buffer needs to be mapped as a 2D texture if set, otherwise must be mapped as an
-        // external texture
-        WRITEABLE = 1 << 1,
-    };
-    // Creates an ExternalTexture for the provided buffer and RenderEngine instance, with the given
-    // usage hint of type Usage.
-    ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine, uint32_t usage);
-
-    ~ExternalTexture();
+    virtual bool hasSameBuffer(const ExternalTexture& other) const = 0;
+    virtual uint32_t getWidth() const = 0;
+    virtual uint32_t getHeight() const = 0;
+    virtual uint64_t getId() const = 0;
+    virtual PixelFormat getPixelFormat() const = 0;
+    virtual uint64_t getUsage() const = 0;
 
     // Retrieves the buffer that is bound to this texture.
-    const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+    virtual const sp<GraphicBuffer>& getBuffer() const = 0;
 
-private:
-    sp<GraphicBuffer> mBuffer;
-    RenderEngine& mRenderEngine;
+    Rect getBounds() const {
+        return {0, 0, static_cast<int32_t>(getWidth()), static_cast<int32_t>(getHeight())};
+    }
     DISALLOW_COPY_AND_ASSIGN(ExternalTexture);
 };
 
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 702e8b0..171cbaa 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -313,6 +313,7 @@
     PrintTo(settings.shadow, os);
     *os << "\n    .stretchEffect = ";
     PrintTo(settings.stretchEffect, os);
+    *os << "\n    .whitePointNits = " << settings.whitePointNits;
     *os << "\n}";
 }
 
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index b9cc648..faa84fc 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -76,6 +76,7 @@
 
 namespace impl {
 class RenderEngine;
+class ExternalTexture;
 }
 
 enum class Protection {
@@ -189,6 +190,10 @@
     static void validateInputBufferUsage(const sp<GraphicBuffer>&);
     static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
 
+    // Allows flinger to get the render engine thread id for power management with ADPF
+    // Returns the tid of the renderengine thread if it's threaded, and std::nullopt otherwise
+    virtual std::optional<pid_t> getRenderEngineTid() const { return std::nullopt; }
+
 protected:
     RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
 
@@ -224,7 +229,7 @@
     // avoid any thread synchronization that may be required by directly calling postRenderCleanup.
     virtual bool canSkipPostRenderCleanup() const = 0;
 
-    friend class ExternalTexture;
+    friend class impl::ExternalTexture;
     friend class threaded::RenderEngineThreaded;
     friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
     const RenderEngineType mRenderEngineType;
diff --git a/libs/renderengine/include/renderengine/impl/ExternalTexture.h b/libs/renderengine/include/renderengine/impl/ExternalTexture.h
new file mode 100644
index 0000000..c0e24f0
--- /dev/null
+++ b/libs/renderengine/include/renderengine/impl/ExternalTexture.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <renderengine/ExternalTexture.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::renderengine::impl {
+
+class RenderEngine;
+
+class ExternalTexture : public android::renderengine::ExternalTexture {
+public:
+    // Usage specifies the rendering intent for the buffer.
+    enum Usage : uint32_t {
+        // When a buffer is not READABLE but is WRITEABLE, then GLESRenderEngine will use that as a
+        // hint to load the buffer into a separate cache
+        READABLE = 1 << 0,
+
+        // The buffer needs to be mapped as a 2D texture if set, otherwise must be mapped as an
+        // external texture
+        WRITEABLE = 1 << 1,
+    };
+
+    // Creates an ExternalTexture for the provided buffer and RenderEngine instance, with the given
+    // usage hint of type Usage.
+    ExternalTexture(const sp<GraphicBuffer>& buffer,
+                    android::renderengine::RenderEngine& renderEngine, uint32_t usage);
+    ~ExternalTexture();
+    const sp<GraphicBuffer>& getBuffer() const override { return mBuffer; };
+    uint32_t getWidth() const override { return getBuffer()->getWidth(); }
+    uint32_t getHeight() const override { return getBuffer()->getHeight(); }
+    uint64_t getId() const override { return getBuffer()->getId(); }
+    PixelFormat getPixelFormat() const override { return getBuffer()->getPixelFormat(); }
+    uint64_t getUsage() const override { return getBuffer()->getUsage(); }
+    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+        return getBuffer() == other.getBuffer();
+    }
+
+private:
+    sp<GraphicBuffer> mBuffer;
+    android::renderengine::RenderEngine& mRenderEngine;
+};
+
+} // namespace android::renderengine::impl
diff --git a/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h b/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h
new file mode 100644
index 0000000..974e0fd
--- /dev/null
+++ b/libs/renderengine/include/renderengine/mock/FakeExternalTexture.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <renderengine/ExternalTexture.h>
+
+namespace android {
+namespace renderengine {
+namespace mock {
+
+class FakeExternalTexture : public renderengine::ExternalTexture {
+    const sp<GraphicBuffer> mNullBuffer = nullptr;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint64_t mId;
+    PixelFormat mPixelFormat;
+    uint64_t mUsage;
+
+public:
+    FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
+                        uint64_t usage)
+          : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
+    const sp<GraphicBuffer>& getBuffer() const { return mNullBuffer; }
+    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+        return getId() == other.getId();
+    }
+    uint32_t getWidth() const override { return mWidth; }
+    uint32_t getHeight() const override { return mHeight; }
+    uint64_t getId() const override { return mId; }
+    PixelFormat getPixelFormat() const override { return mPixelFormat; }
+    uint64_t getUsage() const override { return mUsage; }
+    ~FakeExternalTexture() = default;
+};
+
+} // namespace mock
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index b18a872..a3a1969 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -19,6 +19,7 @@
 #include "android-base/unique_fd.h"
 #include "renderengine/DisplaySettings.h"
 #include "renderengine/LayerSettings.h"
+#include "renderengine/impl/ExternalTexture.h"
 #include "ui/GraphicBuffer.h"
 #include "ui/GraphicTypes.h"
 #include "ui/PixelFormat.h"
@@ -365,8 +366,8 @@
                                   1, usage, "primeShaderCache_dst");
 
         const auto dstTexture =
-                std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
-                                                  ExternalTexture::Usage::WRITEABLE);
+                std::make_shared<impl::ExternalTexture>(dstBuffer, *renderengine,
+                                                        impl::ExternalTexture::Usage::WRITEABLE);
         // This buffer will be the source for the call to drawImageLayers. Draw
         // something to it as a placeholder for what an app draws. We should draw
         // something, but the details are not important. Make use of the shadow layer drawing step
@@ -375,10 +376,10 @@
                 new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
                                   1, usage, "drawImageLayer_src");
 
-        const auto srcTexture =
-                std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
-                                                  ExternalTexture::Usage::READABLE |
-                                                          ExternalTexture::Usage::WRITEABLE);
+        const auto srcTexture = std::make_shared<
+                impl::ExternalTexture>(srcBuffer, *renderengine,
+                                       impl::ExternalTexture::Usage::READABLE |
+                                               impl::ExternalTexture::Usage::WRITEABLE);
         drawHolePunchLayer(renderengine, display, dstTexture);
         drawSolidLayers(renderengine, display, dstTexture);
 
@@ -398,8 +399,8 @@
                 new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
                                   1, usageExternal, "primeShaderCache_external");
         const auto externalTexture =
-                std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
-                                                  ExternalTexture::Usage::READABLE);
+                std::make_shared<impl::ExternalTexture>(externalBuffer, *renderengine,
+                                                        impl::ExternalTexture::Usage::READABLE);
         std::vector<const std::shared_ptr<ExternalTexture>> textures =
             {srcTexture, externalTexture};
 
@@ -412,8 +413,8 @@
         status_t error = f16ExternalBuffer->initCheck();
         if (!error) {
             const auto f16ExternalTexture =
-                std::make_shared<ExternalTexture>(f16ExternalBuffer, *renderengine,
-                                                  ExternalTexture::Usage::READABLE);
+                    std::make_shared<impl::ExternalTexture>(f16ExternalBuffer, *renderengine,
+                                                            impl::ExternalTexture::Usage::READABLE);
             textures.push_back(f16ExternalTexture);
         }
 
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index cc90946..063ce67 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -656,6 +656,7 @@
                                  parameters.layerDimmingRatio, 1.f));
         return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
                                         parameters.display.maxLuminance,
+                                        parameters.display.currentLuminanceNits,
                                         parameters.layer.source.buffer.maxLuminanceNits);
     }
     return parameters.shader;
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 36305ae..6077c2e 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -44,14 +44,15 @@
                                          const shaders::LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
-                                         float maxLuminance) {
+                                         float currentDisplayLuminanceNits, float maxLuminance) {
     ATRACE_CALL();
     SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
 
     effectBuilder.child("child") = shader;
 
-    const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, colorTransform,
-                                                             maxDisplayLuminance, maxLuminance);
+    const auto uniforms =
+            shaders::buildLinearEffectUniforms(linearEffect, colorTransform, maxDisplayLuminance,
+                                               currentDisplayLuminanceNits, maxLuminance);
 
     for (const auto& uniform : uniforms) {
         effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 8eb6670..e0a556b 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -37,13 +37,14 @@
 // matrix transforming from linear XYZ to linear RGB immediately before OETF.
 // We also provide additional HDR metadata upon creating the shader:
 // * The max display luminance is the max luminance of the physical display in nits
+// * The current luminance of the physical display in nits
 // * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086
 // or as the max light level from the CTA 861.3 standard.
 sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
                                          const shaders::LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
-                                         float maxLuminance);
+                                         float currentDisplayLuminanceNits, float maxLuminance);
 } // namespace skia
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index eb2b2dc..2a25b0b 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -26,6 +26,7 @@
 #include <gtest/gtest.h>
 #include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <sync/sync.h>
 #include <system/graphics-base-v1.0.h>
 #include <tonemap/tonemap.h>
@@ -178,7 +179,7 @@
 public:
     std::shared_ptr<renderengine::ExternalTexture> allocateDefaultBuffer() {
         return std::make_shared<
-                renderengine::
+                renderengine::impl::
                         ExternalTexture>(new GraphicBuffer(DEFAULT_DISPLAY_WIDTH,
                                                            DEFAULT_DISPLAY_HEIGHT,
                                                            HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -188,15 +189,16 @@
                                                                    GRALLOC_USAGE_HW_TEXTURE,
                                                            "output"),
                                          *mRE,
-                                         renderengine::ExternalTexture::Usage::READABLE |
-                                                 renderengine::ExternalTexture::Usage::WRITEABLE);
+                                         renderengine::impl::ExternalTexture::Usage::READABLE |
+                                                 renderengine::impl::ExternalTexture::Usage::
+                                                         WRITEABLE);
     }
 
     // Allocates a 1x1 buffer to fill with a solid color
     std::shared_ptr<renderengine::ExternalTexture> allocateSourceBuffer(uint32_t width,
                                                                         uint32_t height) {
         return std::make_shared<
-                renderengine::
+                renderengine::impl::
                         ExternalTexture>(new GraphicBuffer(width, height,
                                                            HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                                            GRALLOC_USAGE_SW_READ_OFTEN |
@@ -204,8 +206,9 @@
                                                                    GRALLOC_USAGE_HW_TEXTURE,
                                                            "input"),
                                          *mRE,
-                                         renderengine::ExternalTexture::Usage::READABLE |
-                                                 renderengine::ExternalTexture::Usage::WRITEABLE);
+                                         renderengine::impl::ExternalTexture::Usage::READABLE |
+                                                 renderengine::impl::ExternalTexture::Usage::
+                                                         WRITEABLE);
     }
 
     std::shared_ptr<renderengine::ExternalTexture> allocateAndFillSourceBuffer(uint32_t width,
@@ -2439,16 +2442,17 @@
     };
 
     auto buf = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1,
-                                                             HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                                             GRALLOC_USAGE_SW_READ_OFTEN |
-                                                                     GRALLOC_USAGE_SW_WRITE_OFTEN |
-                                                                     GRALLOC_USAGE_HW_RENDER |
-                                                                     GRALLOC_USAGE_HW_TEXTURE,
-                                                             "input"),
-                                           *mRE,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                       1,
+                                                       GRALLOC_USAGE_SW_READ_OFTEN |
+                                                               GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                                               GRALLOC_USAGE_HW_RENDER |
+                                                               GRALLOC_USAGE_HW_TEXTURE,
+                                                       "input"),
+                                     *mRE,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     ASSERT_EQ(0, buf->getBuffer()->initCheck());
 
     {
@@ -2472,16 +2476,17 @@
     }
 
     mBuffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1,
-                                                             HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                                             GRALLOC_USAGE_SW_READ_OFTEN |
-                                                                     GRALLOC_USAGE_SW_WRITE_OFTEN |
-                                                                     GRALLOC_USAGE_HW_RENDER |
-                                                                     GRALLOC_USAGE_HW_TEXTURE,
-                                                             "output"),
-                                           *mRE,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                       1,
+                                                       GRALLOC_USAGE_SW_READ_OFTEN |
+                                                               GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                                               GRALLOC_USAGE_HW_RENDER |
+                                                               GRALLOC_USAGE_HW_TEXTURE,
+                                                       "output"),
+                                     *mRE,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     ASSERT_EQ(0, mBuffer->getBuffer()->initCheck());
 
     const renderengine::LayerSettings layer{
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index db7e12b..9685189 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -17,6 +17,7 @@
 #include <cutils/properties.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include "../threaded/RenderEngineThreaded.h"
 
@@ -174,9 +175,10 @@
     renderengine::DisplaySettings settings;
     std::vector<renderengine::LayerSettings> layers;
     std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
 
     base::unique_fd bufferFence;
 
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 08fc814..a7176d3 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -389,6 +389,20 @@
     mCondition.notify_one();
 }
 
+std::optional<pid_t> RenderEngineThreaded::getRenderEngineTid() const {
+    std::promise<pid_t> tidPromise;
+    std::future<pid_t> tidFuture = tidPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&tidPromise](renderengine::RenderEngine& instance) {
+            tidPromise.set_value(gettid());
+        });
+    }
+
+    mCondition.notify_one();
+    return std::make_optional(tidFuture.get());
+}
+
 } // namespace threaded
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 0159cfa..1ba72dd 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -66,6 +66,7 @@
     int getContextPriority() override;
     bool supportsBackgroundBlur() override;
     void onActiveDisplaySizeChanged(ui::Size size) override;
+    std::optional<pid_t> getRenderEngineTid() const override;
 
 protected:
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 0a49008..da88e85 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -276,6 +276,10 @@
         mStringType = SENSOR_STRING_TYPE_HINGE_ANGLE;
         mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
         break;
+    case SENSOR_TYPE_HEAD_TRACKER:
+        mStringType = SENSOR_STRING_TYPE_HEAD_TRACKER;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
     default:
         // Only pipe the stringType, requiredPermission and flags for custom sensors.
         if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
@@ -468,7 +472,15 @@
 }
 
 void Sensor::setId(int32_t id) {
-    mUuid.i64[0] = id;
+    mId = id;
+}
+
+int32_t Sensor::getId() const {
+    return mId;
+}
+
+void Sensor::anonymizeUuid() {
+    mUuid.i64[0] = mId;
     mUuid.i64[1] = 0;
 }
 
@@ -485,17 +497,14 @@
     }
 }
 
-int32_t Sensor::getId() const {
-    return int32_t(mUuid.i64[0]);
-}
-
 size_t Sensor::getFlattenedSize() const {
     size_t fixedSize =
             sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) +
             sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) +
             sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) +
             sizeof(mFifoMaxEventCount) + sizeof(mRequiredPermissionRuntime) +
-            sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid);
+            sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) +
+            sizeof(mUuid) + sizeof(mId);
 
     size_t variableSize =
             sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) +
@@ -529,18 +538,8 @@
     FlattenableUtils::write(buffer, size, mRequiredAppOp);
     FlattenableUtils::write(buffer, size, mMaxDelay);
     FlattenableUtils::write(buffer, size, mFlags);
-    if (mUuid.i64[1] != 0) {
-        // We should never hit this case with our current API, but we
-        // could via a careless API change.  If that happens,
-        // this code will keep us from leaking our UUID (while probably
-        // breaking dynamic sensors).  See b/29547335.
-        ALOGW("Sensor with UUID being flattened; sending 0.  Expect "
-              "bad dynamic sensor behavior");
-        uuid_t tmpUuid;  // default constructor makes this 0.
-        FlattenableUtils::write(buffer, size, tmpUuid);
-    } else {
-        FlattenableUtils::write(buffer, size, mUuid);
-    }
+    FlattenableUtils::write(buffer, size, mUuid);
+    FlattenableUtils::write(buffer, size, mId);
     return NO_ERROR;
 }
 
@@ -580,7 +579,7 @@
 
     size_t fixedSize2 =
             sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) +
-            sizeof(mFlags) + sizeof(mUuid);
+            sizeof(mFlags) + sizeof(mUuid) + sizeof(mId);
     if (size < fixedSize2) {
         return NO_MEMORY;
     }
@@ -590,6 +589,7 @@
     FlattenableUtils::read(buffer, size, mMaxDelay);
     FlattenableUtils::read(buffer, size, mFlags);
     FlattenableUtils::read(buffer, size, mUuid);
+    FlattenableUtils::read(buffer, size, mId);
     return NO_ERROR;
 }
 
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h
index 374b68f..bae8a13 100644
--- a/libs/sensor/include/sensor/Sensor.h
+++ b/libs/sensor/include/sensor/Sensor.h
@@ -96,11 +96,8 @@
     bool isDirectChannelTypeSupported(int32_t sharedMemType) const;
     int32_t getReportingMode() const;
 
-    // Note that after setId() has been called, getUuid() no longer
-    // returns the UUID.
-    // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
-    //     make sure setId() doesn't change the UuidIndex.
     const uuid_t& getUuid() const;
+    void  anonymizeUuid();
     int32_t getId() const;
     void setId(int32_t id);
 
@@ -132,10 +129,8 @@
     int32_t mRequiredAppOp;
     int32_t mMaxDelay;
     uint32_t mFlags;
-    // TODO(b/29547335): Get rid of this field and replace with an index.
-    //     The index will be into a separate global vector of UUIDs.
-    //     Also add an mId field (and change flatten/unflatten appropriately).
     uuid_t  mUuid;
+    int32_t mId;
     static void flattenString8(void*& buffer, size_t& size, const String8& string8);
     static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
 };
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
index 712a27a..43828cc 100644
--- a/libs/shaders/include/shaders/shaders.h
+++ b/libs/shaders/include/shaders/shaders.h
@@ -100,6 +100,7 @@
 std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
                                                               const mat4& colorTransform,
                                                               float maxDisplayLuminance,
+                                                              float currentDisplayLuminanceNits,
                                                               float maxLuminance);
 
 } // namespace android::shaders
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index 6019c4a..4d88d5d 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -463,6 +463,7 @@
 std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
                                                               const mat4& colorTransform,
                                                               float maxDisplayLuminance,
+                                                              float currentDisplayLuminanceNits,
                                                               float maxLuminance) {
     std::vector<tonemap::ShaderUniform> uniforms;
     if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
@@ -480,6 +481,7 @@
     }
 
     tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
+                               .currentDisplayLuminanceNits = currentDisplayLuminanceNits,
                                // If the input luminance is unknown, use display luminance (aka,
                                // no-op any luminance changes)
                                // This will be the case for eg screenshots in addition to
diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h
index bd7b72d..6233e6c 100644
--- a/libs/tonemap/include/tonemap/tonemap.h
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -42,7 +42,11 @@
 // This metadata should not be used for manipulating the source code of the shader program directly,
 // as otherwise caching by other system of these shaders may break.
 struct Metadata {
+    // The maximum luminance of the display in nits
     float displayMaxLuminance = 0.0;
+    // The current luminance of the display in nits
+    float currentDisplayLuminanceNits = 0.0;
+    // The maximum luminance of the content in nits
     float contentMaxLuminance = 0.0;
 };
 
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 980fc8b..f5a22ec 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -128,6 +128,7 @@
     srcs: [
         "DebugUtils.cpp",
         "DeviceProductInfo.cpp",
+        "DisplayIdentification.cpp",
         "DisplayMode.cpp",
         "DynamicDisplayInfo.cpp",
         "Fence.cpp",
@@ -165,6 +166,7 @@
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
+        "android.hardware.graphics.allocator-V1-ndk",
         "android.hardware.graphics.common-V3-ndk",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
@@ -172,6 +174,7 @@
         "android.hardware.graphics.mapper@3.0",
         "android.hardware.graphics.mapper@4.0",
         "libbase",
+        "libbinder_ndk",
         "libcutils",
         "libgralloctypes",
         "libhidlbase",
@@ -188,6 +191,7 @@
     ],
 
     static_libs: [
+        "libaidlcommonsupport",
         "libarect",
         "libgrallocusage",
         "libmath",
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
similarity index 86%
rename from services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
rename to libs/ui/DisplayIdentification.cpp
index 83c2b2e..16ed82a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -24,12 +24,63 @@
 
 #include <log/log.h>
 
-#include "DisplayIdentification.h"
-#include "Hash.h"
+#include <ui/DisplayIdentification.h>
 
 namespace android {
 namespace {
 
+template <class T>
+inline T load(const void* p) {
+    static_assert(std::is_integral<T>::value, "T must be integral");
+
+    T r;
+    std::memcpy(&r, p, sizeof(r));
+    return r;
+}
+
+uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
+    return (val >> shift) | (val << (64 - shift));
+}
+
+uint64_t shiftMix(uint64_t val) {
+    return val ^ (val >> 47);
+}
+
+uint64_t hash64Len16(uint64_t u, uint64_t v) {
+    constexpr uint64_t kMul = 0x9ddfea08eb382d69;
+    uint64_t a = (u ^ v) * kMul;
+    a ^= (a >> 47);
+    uint64_t b = (v ^ a) * kMul;
+    b ^= (b >> 47);
+    b *= kMul;
+    return b;
+}
+
+uint64_t hash64Len0To16(const char* s, uint64_t len) {
+    constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
+    constexpr uint64_t k3 = 0xc949d7c7509e6557;
+
+    if (len > 8) {
+        const uint64_t a = load<uint64_t>(s);
+        const uint64_t b = load<uint64_t>(s + len - 8);
+        return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
+    }
+    if (len >= 4) {
+        const uint32_t a = load<uint32_t>(s);
+        const uint32_t b = load<uint32_t>(s + len - 4);
+        return hash64Len16(len + (a << 3), b);
+    }
+    if (len > 0) {
+        const unsigned char a = static_cast<unsigned char>(s[0]);
+        const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
+        const unsigned char c = static_cast<unsigned char>(s[len - 1]);
+        const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
+        const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
+        return shiftMix(y * k2 ^ z * k3) * k2;
+    }
+    return k2;
+}
+
 using byte_view = std::basic_string_view<uint8_t>;
 
 constexpr size_t kEdidBlockSize = 128;
@@ -339,5 +390,13 @@
     return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
 }
 
-} // namespace android
+uint64_t cityHash64Len0To16(std::string_view sv) {
+    auto len = sv.length();
+    if (len > 16) {
+        ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
+        len = 16;
+    }
+    return hash64Len0To16(sv.data(), len);
+}
 
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 8ac08fb..1f8a2f0 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -16,6 +16,12 @@
 
 #define LOG_TAG "Gralloc4"
 
+#include <aidl/android/hardware/graphics/allocator/AllocationError.h>
+#include <aidl/android/hardware/graphics/allocator/AllocationResult.h>
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android/binder_enums.h>
+#include <android/binder_manager.h>
 #include <hidl/ServiceManagement.h>
 #include <hwbinder/IPCThreadState.h>
 #include <ui/Gralloc4.h>
@@ -27,6 +33,8 @@
 #include <sync/sync.h>
 #pragma clang diagnostic pop
 
+using aidl::android::hardware::graphics::allocator::AllocationError;
+using aidl::android::hardware::graphics::allocator::AllocationResult;
 using aidl::android::hardware::graphics::common::ExtendableType;
 using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
 using aidl::android::hardware::graphics::common::StandardMetadataType;
@@ -36,7 +44,10 @@
 using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
 using android::hardware::graphics::mapper::V4_0::Error;
 using android::hardware::graphics::mapper::V4_0::IMapper;
+using AidlIAllocator = ::aidl::android::hardware::graphics::allocator::IAllocator;
+using AidlBufferUsage = ::aidl::android::hardware::graphics::common::BufferUsage;
 using AidlDataspace = ::aidl::android::hardware::graphics::common::Dataspace;
+using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle;
 using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
 using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
 using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
@@ -48,6 +59,7 @@
 namespace {
 
 static constexpr Error kTransactionError = Error::NO_RESOURCES;
+static const auto kAidlAllocatorServiceName = AidlIAllocator::descriptor + std::string("/default");
 
 uint64_t getValidUsageBits() {
     static const uint64_t validUsageBits = []() -> uint64_t {
@@ -61,6 +73,17 @@
     return validUsageBits;
 }
 
+uint64_t getValidUsageBits41() {
+    static const uint64_t validUsageBits = []() -> uint64_t {
+        uint64_t bits = 0;
+        for (const auto bit : ndk::enum_range<AidlBufferUsage>{}) {
+            bits |= static_cast<int64_t>(bit);
+        }
+        return bits;
+    }();
+    return validUsageBits;
+}
+
 static inline IMapper::Rect sGralloc4Rect(const Rect& rect) {
     IMapper::Rect outRect{};
     outRect.left = rect.left;
@@ -81,6 +104,21 @@
     outDescriptorInfo->reservedSize = 0;
 }
 
+// See if gralloc "4.1" is available.
+static bool hasIAllocatorAidl() {
+    // Avoid re-querying repeatedly for this information;
+    static bool sHasIAllocatorAidl = []() -> bool {
+        // TODO: Enable after landing sepolicy changes
+        if constexpr ((true)) return false;
+
+        if (__builtin_available(android 31, *)) {
+            return AServiceManager_isDeclared(kAidlAllocatorServiceName.c_str());
+        }
+        return false;
+    }();
+    return sHasIAllocatorAidl;
+}
+
 } // anonymous namespace
 
 void Gralloc4Mapper::preload() {
@@ -105,6 +143,9 @@
 status_t Gralloc4Mapper::validateBufferDescriptorInfo(
         IMapper::BufferDescriptorInfo* descriptorInfo) const {
     uint64_t validUsageBits = getValidUsageBits();
+    if (hasIAllocatorAidl()) {
+        validUsageBits |= getValidUsageBits41();
+    }
 
     if (descriptorInfo->usage & ~validUsageBits) {
         ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
@@ -941,9 +982,10 @@
     }
     double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;
 
-    *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << allocationSizeKiB
-             << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
-             << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
+    *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << std::fixed
+             << allocationSizeKiB << "KiB, w/h:" << width << "x" << height << ", usage: 0x"
+             << std::hex << usage << std::dec
+             << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
              << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
              << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace) << std::dec
              << ", compressed: ";
@@ -1069,6 +1111,13 @@
         ALOGW("allocator 4.x is not supported");
         return;
     }
+    if (__builtin_available(android 31, *)) {
+        if (hasIAllocatorAidl()) {
+            mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder(
+                    AServiceManager_waitForService(kAidlAllocatorServiceName.c_str())));
+            ALOGE_IF(!mAidlAllocator, "AIDL IAllocator declared but failed to get service");
+        }
+    }
 }
 
 bool Gralloc4Allocator::isLoaded() const {
@@ -1093,6 +1142,52 @@
         return error;
     }
 
+    if (mAidlAllocator) {
+        AllocationResult result;
+        auto status = mAidlAllocator->allocate(descriptor, bufferCount, &result);
+        if (!status.isOk()) {
+            error = status.getExceptionCode();
+            if (error == EX_SERVICE_SPECIFIC) {
+                error = status.getServiceSpecificError();
+            }
+            if (error == OK) {
+                error = UNKNOWN_ERROR;
+            }
+        } else {
+            if (importBuffers) {
+                for (uint32_t i = 0; i < bufferCount; i++) {
+                    error = mMapper.importBuffer(makeFromAidl(result.buffers[i]),
+                                                 &outBufferHandles[i]);
+                    if (error != NO_ERROR) {
+                        for (uint32_t j = 0; j < i; j++) {
+                            mMapper.freeBuffer(outBufferHandles[j]);
+                            outBufferHandles[j] = nullptr;
+                        }
+                        break;
+                    }
+                }
+            } else {
+                for (uint32_t i = 0; i < bufferCount; i++) {
+                    outBufferHandles[i] = dupFromAidl(result.buffers[i]);
+                    if (!outBufferHandles[i]) {
+                        for (uint32_t j = 0; j < i; j++) {
+                            auto buffer = const_cast<native_handle_t*>(outBufferHandles[j]);
+                            native_handle_close(buffer);
+                            native_handle_delete(buffer);
+                            outBufferHandles[j] = nullptr;
+                        }
+                    }
+                }
+            }
+        }
+        *outStride = result.stride;
+        // Release all the resources held by AllocationResult (specifically any remaining FDs)
+        result = {};
+        // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+        hardware::IPCThreadState::self()->flushCommands();
+        return error;
+    }
+
     auto ret = mAllocator->allocate(descriptor, bufferCount,
                                     [&](const auto& tmpError, const auto& tmpStride,
                                         const auto& tmpBuffers) {
diff --git a/libs/ui/StaticDisplayInfo.cpp b/libs/ui/StaticDisplayInfo.cpp
index b66b281..03d15e4 100644
--- a/libs/ui/StaticDisplayInfo.cpp
+++ b/libs/ui/StaticDisplayInfo.cpp
@@ -29,7 +29,8 @@
     return FlattenableHelpers::getFlattenedSize(connectionType) +
             FlattenableHelpers::getFlattenedSize(density) +
             FlattenableHelpers::getFlattenedSize(secure) +
-            FlattenableHelpers::getFlattenedSize(deviceProductInfo);
+            FlattenableHelpers::getFlattenedSize(deviceProductInfo) +
+            FlattenableHelpers::getFlattenedSize(installOrientation);
 }
 
 status_t StaticDisplayInfo::flatten(void* buffer, size_t size) const {
@@ -40,6 +41,7 @@
     RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density));
     RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure));
     RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, installOrientation));
     return OK;
 }
 
@@ -48,6 +50,7 @@
     RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density));
     RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure));
     RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &installOrientation));
     return OK;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
similarity index 93%
rename from services/surfaceflinger/DisplayHardware/DisplayIdentification.h
rename to libs/ui/include/ui/DisplayIdentification.h
index fbea4e5..fc9c0f4 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -31,7 +31,6 @@
 
 namespace android {
 
-
 using DisplayIdentificationData = std::vector<uint8_t>;
 
 struct DisplayIdentificationInfo {
@@ -81,5 +80,7 @@
 
 PhysicalDisplayId getVirtualDisplayId(uint32_t id);
 
-} // namespace android
+// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
+uint64_t cityHash64Len0To16(std::string_view sv);
 
+} // namespace android
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 62f9e4a..6bafcd6 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_UI_GRALLOC4_H
 #define ANDROID_UI_GRALLOC4_H
 
+#include <aidl/android/hardware/graphics/allocator/IAllocator.h>
 #include <android/hardware/graphics/allocator/4.0/IAllocator.h>
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/mapper/4.0/IMapper.h>
@@ -204,6 +205,8 @@
 private:
     const Gralloc4Mapper& mMapper;
     sp<hardware::graphics::allocator::V4_0::IAllocator> mAllocator;
+    // Optional "4.1" allocator
+    std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAidlAllocator;
 };
 
 } // namespace android
diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h
index e86ca29..cc7c869 100644
--- a/libs/ui/include/ui/StaticDisplayInfo.h
+++ b/libs/ui/include/ui/StaticDisplayInfo.h
@@ -19,6 +19,7 @@
 #include <optional>
 
 #include <ui/DeviceProductInfo.h>
+#include <ui/Rotation.h>
 #include <utils/Flattenable.h>
 
 namespace android::ui {
@@ -31,6 +32,7 @@
     float density = 0.f;
     bool secure = false;
     std::optional<DeviceProductInfo> deviceProductInfo;
+    Rotation installOrientation = ROTATION_0;
 
     bool isFixedSize() const { return false; }
     size_t getFlattenedSize() const;
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
similarity index 98%
rename from services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
rename to libs/ui/tests/DisplayIdentification_test.cpp
index cd4a5c9..736979a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -24,12 +24,12 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "DisplayHardware/DisplayIdentification.h"
-#include "DisplayHardware/Hash.h"
+#include <ui/DisplayIdentification.h>
 
 using ::testing::ElementsAre;
 
 namespace android {
+
 namespace {
 
 const unsigned char kInternalEdid[] =
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
index e6fb2c3..d11631b 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -7,7 +7,7 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-cc_library_shared {
+cc_library {
     name: "libaudiomanager",
 
     srcs: [
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index 1e8eb1e..178bc29 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -40,6 +40,7 @@
     bool chargerAcOnline;
     bool chargerUsbOnline;
     bool chargerWirelessOnline;
+    bool chargerDockOnline;
     int maxChargingCurrent;
     int maxChargingVoltage;
     int batteryStatus;
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index b9b6a19..5b4ee21 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -31,6 +31,7 @@
         "libcutils",
         "libgfxstats",
         "libgpumem",
+        "libgpuwork",
         "libgpumemtracer",
         "libgraphicsenv",
         "liblog",
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 52d5d4f..7b9782f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -25,6 +25,7 @@
 #include <binder/PermissionCache.h>
 #include <cutils/properties.h>
 #include <gpumem/GpuMem.h>
+#include <gpuwork/GpuWork.h>
 #include <gpustats/GpuStats.h>
 #include <private/android_filesystem_config.h>
 #include <tracing/GpuMemTracer.h>
@@ -50,13 +51,20 @@
 
 GpuService::GpuService()
       : mGpuMem(std::make_shared<GpuMem>()),
+        mGpuWork(std::make_shared<gpuwork::GpuWork>()),
         mGpuStats(std::make_unique<GpuStats>()),
         mGpuMemTracer(std::make_unique<GpuMemTracer>()) {
-    std::thread asyncInitThread([this]() {
+
+    std::thread gpuMemAsyncInitThread([this]() {
         mGpuMem->initialize();
         mGpuMemTracer->initialize(mGpuMem);
     });
-    asyncInitThread.detach();
+    gpuMemAsyncInitThread.detach();
+
+    std::thread gpuWorkAsyncInitThread([this]() {
+        mGpuWork->initialize();
+    });
+    gpuWorkAsyncInitThread.detach();
 };
 
 void GpuService::setGpuStats(const std::string& driverPackageName,
@@ -124,6 +132,7 @@
         bool dumpDriverInfo = false;
         bool dumpMem = false;
         bool dumpStats = false;
+        bool dumpWork = false;
         size_t numArgs = args.size();
 
         if (numArgs) {
@@ -134,9 +143,11 @@
                     dumpDriverInfo = true;
                 } else if (args[index] == String16("--gpumem")) {
                     dumpMem = true;
+                } else if (args[index] == String16("--gpuwork")) {
+                    dumpWork = true;
                 }
             }
-            dumpAll = !(dumpDriverInfo || dumpMem || dumpStats);
+            dumpAll = !(dumpDriverInfo || dumpMem || dumpStats || dumpWork);
         }
 
         if (dumpAll || dumpDriverInfo) {
@@ -151,6 +162,10 @@
             mGpuStats->dump(args, &result);
             result.append("\n");
         }
+         if (dumpAll || dumpWork) {
+            mGpuWork->dump(args, &result);
+            result.append("\n");
+        }
     }
 
     write(fd, result.c_str(), result.size());
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index 409084b..d7313d1 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -28,6 +28,10 @@
 
 namespace android {
 
+namespace gpuwork {
+class GpuWork;
+}
+
 class GpuMem;
 class GpuStats;
 class GpuMemTracer;
@@ -77,6 +81,7 @@
      * Attributes
      */
     std::shared_ptr<GpuMem> mGpuMem;
+    std::shared_ptr<gpuwork::GpuWork> mGpuWork;
     std::unique_ptr<GpuStats> mGpuStats;
     std::unique_ptr<GpuMemTracer> mGpuMemTracer;
     std::mutex mLock;
diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS
index ac300d0..0ff65bf 100644
--- a/services/gpuservice/OWNERS
+++ b/services/gpuservice/OWNERS
@@ -1,2 +1,6 @@
 chrisforbes@google.com
 lpy@google.com
+alecmouri@google.com
+lfy@google.com
+paulthomson@google.com
+pbaiget@google.com
diff --git a/services/gpuservice/gpuwork/Android.bp b/services/gpuservice/gpuwork/Android.bp
new file mode 100644
index 0000000..89b31a6
--- /dev/null
+++ b/services/gpuservice/gpuwork/Android.bp
@@ -0,0 +1,61 @@
+// Copyright 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_shared {
+    name: "libgpuwork",
+    srcs: [
+        "GpuWork.cpp",
+    ],
+    header_libs: [
+        "gpu_work_structs",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libbpf_bcc",
+        "libbpf_android",
+        "libcutils",
+        "liblog",
+        "libstatslog",
+        "libstatspull",
+        "libutils",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    export_header_lib_headers: [
+        "gpu_work_structs",
+    ],
+    export_shared_lib_headers: [
+        "libbase",
+        "libbpf_android",
+        "libstatspull",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+    required: [
+        "bpfloader",
+        "gpu_work.o",
+    ],
+}
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
new file mode 100644
index 0000000..e7b1cd4
--- /dev/null
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -0,0 +1,504 @@
+/*
+ * 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 "GpuWork"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "gpuwork/GpuWork.h"
+
+#include <android-base/stringprintf.h>
+#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>
+#include <statslog.h>
+#include <unistd.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <bit>
+#include <chrono>
+#include <cstdint>
+#include <limits>
+#include <map>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include "gpuwork/gpu_work.h"
+
+#define MS_IN_NS (1000000)
+
+namespace android {
+namespace gpuwork {
+
+namespace {
+
+// Gets a BPF map from |mapPath|.
+template <class Key, class Value>
+bool getBpfMap(const char* mapPath, bpf::BpfMap<Key, Value>* out) {
+    errno = 0;
+    auto map = bpf::BpfMap<Key, Value>(mapPath);
+    if (!map.isValid()) {
+        ALOGW("Failed to create bpf map from %s [%d(%s)]", mapPath, errno, strerror(errno));
+        return false;
+    }
+    *out = std::move(map);
+    return true;
+}
+
+template <typename SourceType>
+inline int32_t cast_int32(SourceType) = delete;
+
+template <typename SourceType>
+inline int32_t bitcast_int32(SourceType) = delete;
+
+template <>
+inline int32_t bitcast_int32<uint32_t>(uint32_t source) {
+    int32_t result;
+    memcpy(&result, &source, sizeof(result));
+    return result;
+}
+
+template <>
+inline int32_t cast_int32<uint64_t>(uint64_t source) {
+    if (source > std::numeric_limits<int32_t>::max()) {
+        return std::numeric_limits<int32_t>::max();
+    }
+    return static_cast<int32_t>(source);
+}
+
+template <>
+inline int32_t cast_int32<long long>(long long source) {
+    if (source > std::numeric_limits<int32_t>::max()) {
+        return std::numeric_limits<int32_t>::max();
+    } else if (source < std::numeric_limits<int32_t>::min()) {
+        return std::numeric_limits<int32_t>::min();
+    }
+    return static_cast<int32_t>(source);
+}
+
+} // namespace
+
+using base::StringAppendF;
+
+GpuWork::~GpuWork() {
+    // If we created our clearer thread, then we must stop it and join it.
+    if (mMapClearerThread.joinable()) {
+        // Tell the thread to terminate.
+        {
+            std::scoped_lock<std::mutex> lock(mMutex);
+            mIsTerminating = true;
+            mIsTerminatingConditionVariable.notify_all();
+        }
+
+        // Now, we can join it.
+        mMapClearerThread.join();
+    }
+
+    {
+        std::scoped_lock<std::mutex> lock(mMutex);
+        if (mStatsdRegistered) {
+            AStatsManager_clearPullAtomCallback(android::util::GPU_FREQ_TIME_IN_STATE_PER_UID);
+        }
+    }
+
+    bpf_detach_tracepoint("power", "gpu_work_period");
+}
+
+void GpuWork::initialize() {
+    // Make sure BPF programs are loaded.
+    bpf::waitForProgsLoaded();
+
+    waitForPermissions();
+
+    // Get the BPF maps before trying to attach the BPF program; if we can't get
+    // the maps then there is no point in attaching the BPF program.
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+
+        if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_map", &mGpuWorkMap)) {
+            return;
+        }
+
+        if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_global_data", &mGpuWorkGlobalDataMap)) {
+            return;
+        }
+
+        mPreviousMapClearTimePoint = std::chrono::steady_clock::now();
+    }
+
+    // Attach the tracepoint ONLY if we got the map above.
+    if (!attachTracepoint("/sys/fs/bpf/prog_gpu_work_tracepoint_power_gpu_work_period", "power",
+                          "gpu_work_period")) {
+        return;
+    }
+
+    // Create the map clearer thread, and store it to |mMapClearerThread|.
+    std::thread thread([this]() { periodicallyClearMap(); });
+
+    mMapClearerThread.swap(thread);
+
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        AStatsManager_setPullAtomCallback(int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID},
+                                          nullptr, GpuWork::pullAtomCallback, this);
+        mStatsdRegistered = true;
+    }
+
+    ALOGI("Initialized!");
+
+    mInitialized.store(true);
+}
+
+void GpuWork::dump(const Vector<String16>& /* args */, std::string* result) {
+    if (!mInitialized.load()) {
+        result->append("GPU time in state information is not available.\n");
+        return;
+    }
+
+    // Ordered map ensures output data is sorted by UID.
+    std::map<Uid, UidTrackingInfo> dumpMap;
+
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+
+        if (!mGpuWorkMap.isValid()) {
+            result->append("GPU time in state map is not available.\n");
+            return;
+        }
+
+        // Iteration of BPF hash maps can be unreliable (no data races, but elements
+        // may be repeated), as the map is typically being modified by other
+        // threads. The buckets are all preallocated. Our eBPF program only updates
+        // entries (in-place) or adds entries. |GpuWork| only iterates or clears the
+        // map while holding |mMutex|. Given this, we should be able to iterate over
+        // all elements reliably. In the worst case, we might see elements more than
+        // once.
+
+        // Note that userspace reads of BPF maps make a copy of the value, and
+        // thus the returned value is not being concurrently accessed by the BPF
+        // program (no atomic reads needed below).
+
+        mGpuWorkMap.iterateWithValue([&dumpMap](const Uid& key, const UidTrackingInfo& value,
+                                                const android::bpf::BpfMap<Uid, UidTrackingInfo>&)
+                                             -> base::Result<void> {
+            dumpMap[key] = value;
+            return {};
+        });
+    }
+
+    // Find the largest frequency where some UID has spent time in that frequency.
+    size_t largestFrequencyWithTime = 0;
+    for (const auto& uidToUidInfo : dumpMap) {
+        for (size_t i = largestFrequencyWithTime + 1; i < kNumTrackedFrequencies; ++i) {
+            if (uidToUidInfo.second.frequency_times_ns[i] > 0) {
+                largestFrequencyWithTime = i;
+            }
+        }
+    }
+
+    // Dump time in state information.
+    // E.g.
+    // uid/freq: 0MHz 50MHz 100MHz ...
+    // 1000: 0 0 0 0 ...
+    // 1003: 0 0 3456 0 ...
+    // [errors:3]1006: 0 0 3456 0 ...
+
+    // Header.
+    result->append("GPU time in frequency state in ms.\n");
+    result->append("uid/freq: 0MHz");
+    for (size_t i = 1; i <= largestFrequencyWithTime; ++i) {
+        StringAppendF(result, " %zuMHz", i * 50);
+    }
+    result->append("\n");
+
+    for (const auto& uidToUidInfo : dumpMap) {
+        if (uidToUidInfo.second.error_count) {
+            StringAppendF(result, "[errors:%" PRIu32 "]", uidToUidInfo.second.error_count);
+        }
+        StringAppendF(result, "%" PRIu32 ":", uidToUidInfo.first);
+        for (size_t i = 0; i <= largestFrequencyWithTime; ++i) {
+            StringAppendF(result, " %" PRIu64,
+                          uidToUidInfo.second.frequency_times_ns[i] / MS_IN_NS);
+        }
+        result->append("\n");
+    }
+}
+
+bool GpuWork::attachTracepoint(const char* programPath, const char* tracepointGroup,
+                               const char* tracepointName) {
+    errno = 0;
+    base::unique_fd fd(bpf::retrieveProgram(programPath));
+    if (fd < 0) {
+        ALOGW("Failed to retrieve pinned program from %s [%d(%s)]", programPath, errno,
+              strerror(errno));
+        return false;
+    }
+
+    // Attach the program to the tracepoint. The tracepoint is automatically enabled.
+    errno = 0;
+    int count = 0;
+    while (bpf_attach_tracepoint(fd.get(), tracepointGroup, tracepointName) < 0) {
+        if (++count > kGpuWaitTimeoutSeconds) {
+            ALOGW("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", tracepointGroup,
+                  tracepointName, errno, strerror(errno));
+            return false;
+        }
+        // Retry until GPU driver loaded or timeout.
+        sleep(1);
+        errno = 0;
+    }
+
+    return true;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuWork::pullAtomCallback(int32_t atomTag,
+                                                               AStatsEventList* data,
+                                                               void* cookie) {
+    ATRACE_CALL();
+
+    GpuWork* gpuWork = reinterpret_cast<GpuWork*>(cookie);
+    if (atomTag == android::util::GPU_FREQ_TIME_IN_STATE_PER_UID) {
+        return gpuWork->pullFrequencyAtoms(data);
+    }
+
+    return AStatsManager_PULL_SKIP;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuWork::pullFrequencyAtoms(AStatsEventList* data) {
+    ATRACE_CALL();
+
+    if (!data || !mInitialized.load()) {
+        return AStatsManager_PULL_SKIP;
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    if (!mGpuWorkMap.isValid()) {
+        return AStatsManager_PULL_SKIP;
+    }
+
+    std::unordered_map<Uid, UidTrackingInfo> uidInfos;
+
+    // Iteration of BPF hash maps can be unreliable (no data races, but elements
+    // may be repeated), as the map is typically being modified by other
+    // threads. The buckets are all preallocated. Our eBPF program only updates
+    // entries (in-place) or adds entries. |GpuWork| only iterates or clears the
+    // map while holding |mMutex|. Given this, we should be able to iterate over
+    // all elements reliably. In the worst case, we might see elements more than
+    // once.
+
+    // Note that userspace reads of BPF maps make a copy of the value, and thus
+    // the returned value is not being concurrently accessed by the BPF program
+    // (no atomic reads needed below).
+
+    mGpuWorkMap.iterateWithValue(
+            [&uidInfos](const Uid& key, const UidTrackingInfo& value,
+                        const android::bpf::BpfMap<Uid, UidTrackingInfo>&) -> base::Result<void> {
+                uidInfos[key] = value;
+                return {};
+            });
+
+    ALOGI("pullFrequencyAtoms: uidInfos.size() == %zu", uidInfos.size());
+
+    // Get a list of just the UIDs; the order does not matter.
+    std::vector<Uid> uids;
+    for (const auto& pair : uidInfos) {
+        uids.push_back(pair.first);
+    }
+
+    std::random_device device;
+    std::default_random_engine random_engine(device());
+
+    // If we have more than |kNumSampledUids| UIDs, choose |kNumSampledUids|
+    // random UIDs. We swap them to the front of the list. Given the list
+    // indices 0..i..n-1, we have the following inclusive-inclusive ranges:
+    // - [0, i-1] == the randomly chosen elements.
+    // - [i, n-1] == the remaining unchosen elements.
+    if (uids.size() > kNumSampledUids) {
+        for (size_t i = 0; i < kNumSampledUids; ++i) {
+            std::uniform_int_distribution<size_t> uniform_dist(i, uids.size() - 1);
+            size_t random_index = uniform_dist(random_engine);
+            std::swap(uids[i], uids[random_index]);
+        }
+        // Only keep the front |kNumSampledUids| elements.
+        uids.resize(kNumSampledUids);
+    }
+
+    ALOGI("pullFrequencyAtoms: uids.size() == %zu", uids.size());
+
+    auto now = std::chrono::steady_clock::now();
+
+    int32_t duration = cast_int32(
+            std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint)
+                    .count());
+
+    for (const Uid uid : uids) {
+        const UidTrackingInfo& info = uidInfos[uid];
+        ALOGI("pullFrequencyAtoms: adding stats for UID %" PRIu32, uid);
+        android::util::addAStatsEvent(data, int32_t{android::util::GPU_FREQ_TIME_IN_STATE_PER_UID},
+                                      // uid
+                                      bitcast_int32(uid),
+                                      // time_duration_seconds
+                                      int32_t{duration},
+                                      // max_freq_mhz
+                                      int32_t{1000},
+                                      // freq_0_mhz_time_millis
+                                      cast_int32(info.frequency_times_ns[0] / 1000000),
+                                      // freq_50_mhz_time_millis
+                                      cast_int32(info.frequency_times_ns[1] / 1000000),
+                                      // ... etc. ...
+                                      cast_int32(info.frequency_times_ns[2] / 1000000),
+                                      cast_int32(info.frequency_times_ns[3] / 1000000),
+                                      cast_int32(info.frequency_times_ns[4] / 1000000),
+                                      cast_int32(info.frequency_times_ns[5] / 1000000),
+                                      cast_int32(info.frequency_times_ns[6] / 1000000),
+                                      cast_int32(info.frequency_times_ns[7] / 1000000),
+                                      cast_int32(info.frequency_times_ns[8] / 1000000),
+                                      cast_int32(info.frequency_times_ns[9] / 1000000),
+                                      cast_int32(info.frequency_times_ns[10] / 1000000),
+                                      cast_int32(info.frequency_times_ns[11] / 1000000),
+                                      cast_int32(info.frequency_times_ns[12] / 1000000),
+                                      cast_int32(info.frequency_times_ns[13] / 1000000),
+                                      cast_int32(info.frequency_times_ns[14] / 1000000),
+                                      cast_int32(info.frequency_times_ns[15] / 1000000),
+                                      cast_int32(info.frequency_times_ns[16] / 1000000),
+                                      cast_int32(info.frequency_times_ns[17] / 1000000),
+                                      cast_int32(info.frequency_times_ns[18] / 1000000),
+                                      cast_int32(info.frequency_times_ns[19] / 1000000),
+                                      // freq_1000_mhz_time_millis
+                                      cast_int32(info.frequency_times_ns[20] / 1000000));
+    }
+    clearMap();
+    return AStatsManager_PULL_SUCCESS;
+}
+
+void GpuWork::periodicallyClearMap() {
+    std::unique_lock<std::mutex> lock(mMutex);
+
+    auto previousTime = std::chrono::steady_clock::now();
+
+    while (true) {
+        if (mIsTerminating) {
+            break;
+        }
+        auto nextTime = std::chrono::steady_clock::now();
+        auto differenceSeconds =
+                std::chrono::duration_cast<std::chrono::seconds>(nextTime - previousTime);
+        if (differenceSeconds.count() > kMapClearerWaitDurationSeconds) {
+            // It has been >1 hour, so clear the map, if needed.
+            clearMapIfNeeded();
+            // We only update |previousTime| if we actually checked the map.
+            previousTime = nextTime;
+        }
+        // Sleep for ~1 hour. It does not matter if we don't check the map for 2
+        // hours.
+        mIsTerminatingConditionVariable.wait_for(lock,
+                                                 std::chrono::seconds{
+                                                         kMapClearerWaitDurationSeconds});
+    }
+}
+
+void GpuWork::clearMapIfNeeded() {
+    if (!mInitialized.load() || !mGpuWorkMap.isValid() || !mGpuWorkGlobalDataMap.isValid()) {
+        ALOGW("Map clearing could not occur because we are not initialized properly");
+        return;
+    }
+
+    base::Result<GlobalData> globalData = mGpuWorkGlobalDataMap.readValue(0);
+    if (!globalData.ok()) {
+        ALOGW("Could not read BPF global data map entry");
+        return;
+    }
+
+    // Note that userspace reads of BPF maps make a copy of the value, and thus
+    // the return value is not being concurrently accessed by the BPF program
+    // (no atomic reads needed below).
+
+    uint64_t numEntries = globalData.value().num_map_entries;
+
+    // If the map is <=75% full, we do nothing.
+    if (numEntries <= (kMaxTrackedUids / 4) * 3) {
+        return;
+    }
+
+    clearMap();
+}
+
+void GpuWork::clearMap() {
+    if (!mInitialized.load() || !mGpuWorkMap.isValid() || !mGpuWorkGlobalDataMap.isValid()) {
+        ALOGW("Map clearing could not occur because we are not initialized properly");
+        return;
+    }
+
+    base::Result<GlobalData> globalData = mGpuWorkGlobalDataMap.readValue(0);
+    if (!globalData.ok()) {
+        ALOGW("Could not read BPF global data map entry");
+        return;
+    }
+
+    // Iterating BPF maps to delete keys is tricky. If we just repeatedly call
+    // |getFirstKey()| and delete that, we may loop forever (or for a long time)
+    // because our BPF program might be repeatedly re-adding UID keys. Also,
+    // even if we limit the number of elements we try to delete, we might only
+    // delete new entries, leaving old entries in the map. If we delete a key A
+    // and then call |getNextKey(A)|, the first key in the map is returned, so
+    // we have the same issue.
+    //
+    // Thus, we instead get the next key and then delete the previous key. We
+    // also limit the number of deletions we try, just in case.
+
+    base::Result<Uid> key = mGpuWorkMap.getFirstKey();
+
+    for (size_t i = 0; i < kMaxTrackedUids; ++i) {
+        if (!key.ok()) {
+            break;
+        }
+        base::Result<Uid> previousKey = key;
+        key = mGpuWorkMap.getNextKey(previousKey.value());
+        mGpuWorkMap.deleteValue(previousKey.value());
+    }
+
+    // Reset our counter; |globalData| is a copy of the data, so we have to use
+    // |writeValue|.
+    globalData.value().num_map_entries = 0;
+    mGpuWorkGlobalDataMap.writeValue(0, globalData.value(), BPF_ANY);
+
+    // Update |mPreviousMapClearTimePoint| so we know when we started collecting
+    // the stats.
+    mPreviousMapClearTimePoint = std::chrono::steady_clock::now();
+}
+
+void GpuWork::waitForPermissions() {
+    const String16 permissionRegisterStatsPullAtom(kPermissionRegisterStatsPullAtom);
+    int count = 0;
+    while (!PermissionCache::checkPermission(permissionRegisterStatsPullAtom, getpid(), getuid())) {
+        if (++count > kPermissionsWaitTimeoutSeconds) {
+            ALOGW("Timed out waiting for android.permission.REGISTER_STATS_PULL_ATOM");
+            return;
+        }
+        // Retry.
+        sleep(1);
+    }
+}
+
+} // namespace gpuwork
+} // namespace android
diff --git a/services/gpuservice/gpuwork/bpfprogs/Android.bp b/services/gpuservice/gpuwork/bpfprogs/Android.bp
new file mode 100644
index 0000000..b3c4eff
--- /dev/null
+++ b/services/gpuservice/gpuwork/bpfprogs/Android.bp
@@ -0,0 +1,35 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+bpf {
+    name: "gpu_work.o",
+    srcs: ["gpu_work.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
+
+cc_library_headers {
+    name: "gpu_work_structs",
+    export_include_dirs: ["include"],
+}
diff --git a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
new file mode 100644
index 0000000..a0e1b22
--- /dev/null
+++ b/services/gpuservice/gpuwork/bpfprogs/gpu_work.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/gpuwork/gpu_work.h"
+
+#include <linux/bpf.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef MOCK_BPF
+#include <test/mock_bpf_helpers.h>
+#else
+#include <bpf_helpers.h>
+#endif
+
+#define S_IN_NS (1000000000)
+#define MHZ_IN_KHZS (1000)
+
+typedef uint32_t Uid;
+
+// A map from UID to |UidTrackingInfo|.
+DEFINE_BPF_MAP_GRW(gpu_work_map, HASH, Uid, UidTrackingInfo, kMaxTrackedUids, AID_GRAPHICS);
+
+// A map containing a single entry of |GlobalData|.
+DEFINE_BPF_MAP_GRW(gpu_work_global_data, ARRAY, uint32_t, GlobalData, 1, AID_GRAPHICS);
+
+// GpuUidWorkPeriodEvent defines the structure of a kernel tracepoint under the
+// tracepoint system (also referred to as the group) "power" and name
+// "gpu_work_period". In summary, the kernel tracepoint should be
+// "power/gpu_work_period", available at:
+//
+//  /sys/kernel/tracing/events/power/gpu_work_period/
+//
+// GpuUidWorkPeriodEvent defines a non-overlapping, non-zero period of time when
+// work was running on the GPU for a given application (identified by its UID;
+// the persistent, unique ID of the application) from |start_time_ns| to
+// |end_time_ns|. Drivers should issue this tracepoint as soon as possible
+// (within 1 second) after |end_time_ns|. For a given UID, periods must not
+// overlap, but periods from different UIDs can overlap (and should overlap, if
+// and only if that is how the work was executed). The period includes
+// information such as |frequency_khz|, the frequency that the GPU was running
+// at during the period, and |includes_compute_work|, whether the work included
+// compute shader work (not just graphics work). GPUs may have multiple
+// frequencies that can be adjusted, but the driver should report the frequency
+// that most closely estimates power usage (e.g. the frequency of shader cores,
+// not a scheduling unit).
+//
+// If any information changes while work from the UID is running on the GPU
+// (e.g. the GPU frequency changes, or the work starts/stops including compute
+// work) then the driver must conceptually end the period, issue the tracepoint,
+// start tracking a new period, and eventually issue a second tracepoint when
+// the work completes or when the information changes again. In this case, the
+// |end_time_ns| of the first period must equal the |start_time_ns| of the
+// second period. The driver may also end and start a new period (without a
+// gap), even if no information changes. For example, this might be convenient
+// if there is a collection of work from a UID running on the GPU for a long
+// time; ending and starting a period as individual parts of the work complete
+// allows the consumer of the tracepoint to be updated about the ongoing work.
+//
+// For a given UID, the tracepoints must be emitted in order; that is, the
+// |start_time_ns| of each subsequent tracepoint for a given UID must be
+// monotonically increasing.
+typedef struct {
+    // Actual fields start at offset 8.
+    uint64_t reserved;
+
+    // The UID of the process (i.e. persistent, unique ID of the Android app)
+    // that submitted work to the GPU.
+    uint32_t uid;
+
+    // The GPU frequency during the period. GPUs may have multiple frequencies
+    // that can be adjusted, but the driver should report the frequency that
+    // would most closely estimate power usage (e.g. the frequency of shader
+    // cores, not a scheduling unit).
+    uint32_t frequency_khz;
+
+    // The start time of the period in nanoseconds. The clock must be
+    // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function.
+    uint64_t start_time_ns;
+
+    // The end time of the period in nanoseconds. The clock must be
+    // CLOCK_MONOTONIC, as returned by the ktime_get_ns(void) function.
+    uint64_t end_time_ns;
+
+    // Flags about the work. Reserved for future use.
+    uint64_t flags;
+
+    // The maximum GPU frequency allowed during the period according to the
+    // thermal throttling policy. Must be 0 if no thermal throttling was
+    // enforced during the period. The value must relate to |frequency_khz|; in
+    // other words, if |frequency_khz| is the frequency of the GPU shader cores,
+    // then |thermally_throttled_max_frequency_khz| must be the maximum allowed
+    // frequency of the GPU shader cores (not the maximum allowed frequency of
+    // some other part of the GPU).
+    //
+    // Note: Unlike with other fields of this struct, if the
+    // |thermally_throttled_max_frequency_khz| value conceptually changes while
+    // work from a UID is running on the GPU then the GPU driver does NOT need
+    // to accurately report this by ending the period and starting to track a
+    // new period; instead, the GPU driver may report any one of the
+    // |thermally_throttled_max_frequency_khz| values that was enforced during
+    // the period. The motivation for this relaxation is that we assume the
+    // maximum frequency will not be changing rapidly, and so capturing the
+    // exact point at which the change occurs is unnecessary.
+    uint32_t thermally_throttled_max_frequency_khz;
+
+} GpuUidWorkPeriodEvent;
+
+_Static_assert(offsetof(GpuUidWorkPeriodEvent, uid) == 8 &&
+                       offsetof(GpuUidWorkPeriodEvent, frequency_khz) == 12 &&
+                       offsetof(GpuUidWorkPeriodEvent, start_time_ns) == 16 &&
+                       offsetof(GpuUidWorkPeriodEvent, end_time_ns) == 24 &&
+                       offsetof(GpuUidWorkPeriodEvent, flags) == 32 &&
+                       offsetof(GpuUidWorkPeriodEvent, thermally_throttled_max_frequency_khz) == 40,
+               "Field offsets of struct GpuUidWorkPeriodEvent must not be changed because they "
+               "must match the tracepoint field offsets found via adb shell cat "
+               "/sys/kernel/tracing/events/power/gpu_work_period/format");
+
+DEFINE_BPF_PROG("tracepoint/power/gpu_work_period", AID_ROOT, AID_GRAPHICS, tp_gpu_work_period)
+(GpuUidWorkPeriodEvent* const args) {
+    // Note: In BPF programs, |__sync_fetch_and_add| is translated to an atomic
+    // add.
+
+    const uint32_t uid = args->uid;
+
+    // Get |UidTrackingInfo| for |uid|.
+    UidTrackingInfo* uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid);
+    if (!uid_tracking_info) {
+        // There was no existing entry, so we add a new one.
+        UidTrackingInfo initial_info;
+        __builtin_memset(&initial_info, 0, sizeof(initial_info));
+        if (0 == bpf_gpu_work_map_update_elem(&uid, &initial_info, BPF_NOEXIST)) {
+            // We added an entry to the map, so we increment our entry counter in
+            // |GlobalData|.
+            const uint32_t zero = 0;
+            // Get the |GlobalData|.
+            GlobalData* global_data = bpf_gpu_work_global_data_lookup_elem(&zero);
+            // Getting the global data never fails because it is an |ARRAY| map,
+            // but we need to keep the verifier happy.
+            if (global_data) {
+                __sync_fetch_and_add(&global_data->num_map_entries, 1);
+            }
+        }
+        uid_tracking_info = bpf_gpu_work_map_lookup_elem(&uid);
+        if (!uid_tracking_info) {
+            // This should never happen, unless entries are getting deleted at
+            // this moment. If so, we just give up.
+            return 0;
+        }
+    }
+
+    // The period duration must be non-zero.
+    if (args->start_time_ns >= args->end_time_ns) {
+        __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
+        return 0;
+    }
+
+    // The frequency must not be 0.
+    if (args->frequency_khz == 0) {
+        __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
+        return 0;
+    }
+
+    // Calculate the frequency index: see |UidTrackingInfo.frequency_times_ns|.
+    // Round to the nearest 50MHz bucket.
+    uint32_t frequency_index =
+            ((args->frequency_khz / MHZ_IN_KHZS) + (kFrequencyGranularityMhz / 2)) /
+            kFrequencyGranularityMhz;
+    if (frequency_index >= kNumTrackedFrequencies) {
+        frequency_index = kNumTrackedFrequencies - 1;
+    }
+
+    // Never round down to 0MHz, as this is a special bucket (see below) and not
+    // an actual operating point.
+    if (frequency_index == 0) {
+        frequency_index = 1;
+    }
+
+    // Update time in state.
+    __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[frequency_index],
+                         args->end_time_ns - args->start_time_ns);
+
+    if (uid_tracking_info->previous_end_time_ns > args->start_time_ns) {
+        // This must not happen because per-UID periods must not overlap and
+        // must be emitted in order.
+        __sync_fetch_and_add(&uid_tracking_info->error_count, 1);
+    } else {
+        // The period appears to have been emitted after the previous, as
+        // expected, so we can calculate the gap between this and the previous
+        // period.
+        const uint64_t gap_time = args->start_time_ns - uid_tracking_info->previous_end_time_ns;
+
+        // Update |previous_end_time_ns|.
+        uid_tracking_info->previous_end_time_ns = args->end_time_ns;
+
+        // Update the special 0MHz frequency time, which stores the gaps between
+        // periods, but only if the gap is < 1 second.
+        if (gap_time > 0 && gap_time < S_IN_NS) {
+            __sync_fetch_and_add(&uid_tracking_info->frequency_times_ns[0], gap_time);
+        }
+    }
+
+    return 0;
+}
+
+LICENSE("Apache 2.0");
diff --git a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
new file mode 100644
index 0000000..4fe8464
--- /dev/null
+++ b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+#include <type_traits>
+
+namespace android {
+namespace gpuwork {
+#endif
+
+typedef struct {
+    // The end time of the previous period from this UID in nanoseconds.
+    uint64_t previous_end_time_ns;
+
+    // The time spent at each GPU frequency while running GPU work from the UID,
+    // in nanoseconds. Array index i stores the time for frequency i*50 MHz. So
+    // index 0 is 0Mhz, index 1 is 50MHz, index 2 is 100MHz, etc., up to index
+    // |kNumTrackedFrequencies|.
+    uint64_t frequency_times_ns[21];
+
+    // The number of times we received |GpuUidWorkPeriodEvent| events in an
+    // unexpected order. See |GpuUidWorkPeriodEvent|.
+    uint32_t error_count;
+
+} UidTrackingInfo;
+
+typedef struct {
+    // We cannot query the number of entries in BPF map |gpu_work_map|. We track
+    // the number of entries (approximately) using a counter so we can check if
+    // the map is nearly full.
+    uint64_t num_map_entries;
+} GlobalData;
+
+static const uint32_t kMaxTrackedUids = 512;
+static const uint32_t kFrequencyGranularityMhz = 50;
+static const uint32_t kNumTrackedFrequencies = 21;
+
+#ifdef __cplusplus
+static_assert(kNumTrackedFrequencies ==
+              std::extent<decltype(UidTrackingInfo::frequency_times_ns)>::value);
+
+} // namespace gpuwork
+} // namespace android
+#endif
diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
new file mode 100644
index 0000000..b6f493d
--- /dev/null
+++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bpf/BpfMap.h>
+#include <stats_pull_atom_callback.h>
+#include <utils/Mutex.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <condition_variable>
+#include <cstdint>
+#include <functional>
+#include <thread>
+
+#include "gpuwork/gpu_work.h"
+
+namespace android {
+namespace gpuwork {
+
+class GpuWork {
+public:
+    using Uid = uint32_t;
+
+    GpuWork() = default;
+    ~GpuWork();
+
+    void initialize();
+
+    // Dumps the GPU time in frequency state information.
+    void dump(const Vector<String16>& args, std::string* result);
+
+private:
+    // Attaches tracepoint |tracepoint_group|/|tracepoint_name| to BPF program at path
+    // |program_path|. The tracepoint is also enabled.
+    static bool attachTracepoint(const char* program_path, const char* tracepoint_group,
+                                 const char* tracepoint_name);
+
+    // Native atom puller callback registered in statsd.
+    static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
+                                                                 AStatsEventList* data,
+                                                                 void* cookie);
+
+    AStatsManager_PullAtomCallbackReturn pullFrequencyAtoms(AStatsEventList* data);
+
+    // Periodically calls |clearMapIfNeeded| to clear the |mGpuWorkMap| map, if
+    // needed.
+    //
+    // Thread safety analysis is skipped because we need to use
+    // |std::unique_lock|, which is not currently supported by thread safety
+    // analysis.
+    void periodicallyClearMap() NO_THREAD_SAFETY_ANALYSIS;
+
+    // Checks whether the |mGpuWorkMap| map is nearly full and, if so, clears
+    // it.
+    void clearMapIfNeeded() REQUIRES(mMutex);
+
+    // Clears the |mGpuWorkMap| map.
+    void clearMap() REQUIRES(mMutex);
+
+    // Waits for required permissions to become set. This seems to be needed
+    // because platform service permissions might not be set when a service
+    // first starts. See b/214085769.
+    void waitForPermissions();
+
+    // Indicates whether our eBPF components have been initialized.
+    std::atomic<bool> mInitialized = false;
+
+    // A thread that periodically checks whether |mGpuWorkMap| is nearly full
+    // and, if so, clears it.
+    std::thread mMapClearerThread;
+
+    // Mutex for |mGpuWorkMap| and a few other fields.
+    std::mutex mMutex;
+
+    // BPF map for per-UID GPU work.
+    bpf::BpfMap<Uid, UidTrackingInfo> mGpuWorkMap GUARDED_BY(mMutex);
+
+    // BPF map containing a single element for global data.
+    bpf::BpfMap<uint32_t, GlobalData> mGpuWorkGlobalDataMap GUARDED_BY(mMutex);
+
+    // When true, we are being destructed, so |mMapClearerThread| should stop.
+    bool mIsTerminating GUARDED_BY(mMutex);
+
+    // A condition variable for |mIsTerminating|.
+    std::condition_variable mIsTerminatingConditionVariable GUARDED_BY(mMutex);
+
+    // 30 second timeout for trying to attach a BPF program to a tracepoint.
+    static constexpr int kGpuWaitTimeoutSeconds = 30;
+
+    // The wait duration for the map clearer thread; the thread checks the map
+    // every ~1 hour.
+    static constexpr uint32_t kMapClearerWaitDurationSeconds = 60 * 60;
+
+    // Whether our |pullAtomCallback| function is registered.
+    bool mStatsdRegistered GUARDED_BY(mMutex) = false;
+
+    // The number of randomly chosen (i.e. sampled) UIDs to log stats for.
+    static constexpr int kNumSampledUids = 10;
+
+    // The previous time point at which |mGpuWorkMap| was cleared.
+    std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex);
+
+    // Permission to register a statsd puller.
+    static constexpr char16_t kPermissionRegisterStatsPullAtom[] =
+            u"android.permission.REGISTER_STATS_PULL_ATOM";
+
+    // Time limit for waiting for permissions.
+    static constexpr int kPermissionsWaitTimeoutSeconds = 30;
+};
+
+} // namespace gpuwork
+} // namespace android
diff --git a/services/gpuservice/vts/Android.bp b/services/gpuservice/vts/Android.bp
new file mode 100644
index 0000000..83a40e7
--- /dev/null
+++ b/services/gpuservice/vts/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+java_test_host {
+    name: "GpuServiceVendorTests",
+    srcs: ["src/**/*.java"],
+    libs: [
+        "tradefed",
+        "vts-core-tradefed-harness",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/services/gpuservice/vts/AndroidTest.xml b/services/gpuservice/vts/AndroidTest.xml
new file mode 100644
index 0000000..02ca07f
--- /dev/null
+++ b/services/gpuservice/vts/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs GpuServiceVendorTests">
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="GpuServiceVendorTests.jar" />
+    </test>
+</configuration>
diff --git a/services/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
new file mode 100644
index 0000000..e789052
--- /dev/null
+++ b/services/gpuservice/vts/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 653544
+paulthomson@google.com
+pbaiget@google.com
+lfy@google.com
+chrisforbes@google.com
+lpy@google.com
+alecmouri@google.com
diff --git a/services/gpuservice/vts/TEST_MAPPING b/services/gpuservice/vts/TEST_MAPPING
new file mode 100644
index 0000000..b33e962
--- /dev/null
+++ b/services/gpuservice/vts/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "GpuServiceVendorTests"
+    }
+  ]
+}
diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
new file mode 100644
index 0000000..4a77d9a
--- /dev/null
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+package com.android.tests.gpuservice;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class GpuWorkTracepointTest extends BaseHostJUnit4Test {
+
+    private static final String CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH =
+            "/sys/kernel/tracing/events/power/cpu_frequency/format";
+    private static final String GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH =
+            "/sys/kernel/tracing/events/power/gpu_work_period/format";
+
+    @Test
+    public void testReadTracingEvents() throws Exception {
+        // Test |testGpuWorkPeriodTracepointFormat| is dependent on whether certain tracepoint
+        // paths exist. This means the test will vacuously pass if the tracepoint file system is
+        // inaccessible. Thus, as a basic check, we make sure the CPU frequency tracepoint format
+        // is accessible. If not, something is probably fundamentally broken about the tracing
+        // file system.
+        CommandResult commandResult = getDevice().executeShellV2Command(
+                String.format("cat %s", CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH));
+
+        assertEquals(String.format(
+                        "Failed to read \"%s\". This probably means that the tracing file system "
+                                + "is fundamentally broken in some way, possibly due to bad "
+                                + "permissions.",
+                        CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH),
+                commandResult.getStatus(), CommandStatus.SUCCESS);
+    }
+
+    @Test
+    public void testGpuWorkPeriodTracepointFormat() throws Exception {
+        CommandResult commandResult = getDevice().executeShellV2Command(
+                String.format("cat %s", GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH));
+
+        // If we failed to cat the tracepoint format then the test ends here.
+        assumeTrue(String.format("Failed to cat the gpu_work_period tracepoint format at %s",
+                        GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH),
+                commandResult.getStatus().equals(CommandStatus.SUCCESS));
+
+        // Otherwise, we check that the fields match the expected fields.
+        String actualFields = Arrays.stream(
+                commandResult.getStdout().trim().split("\n")).filter(
+                s -> s.startsWith("\tfield:")).collect(
+                Collectors.joining("\n"));
+
+        String expectedFields = String.join("\n",
+                "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;",
+                "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;",
+                "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;",
+                "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;",
+                "\tfield:u32 uid;\toffset:8;\tsize:4;\tsigned:0;",
+                "\tfield:u32 frequency;\toffset:12;\tsize:4;\tsigned:0;",
+                "\tfield:u64 start_time;\toffset:16;\tsize:8;\tsigned:0;",
+                "\tfield:u64 end_time;\toffset:24;\tsize:8;\tsigned:0;",
+                "\tfield:u64 flags;\toffset:32;\tsize:8;\tsigned:0;",
+                "\tfield:u32 thermally_throttled_max_frequency_khz;\toffset:40;\tsize:4;\tsigned:0;"
+        );
+
+        // We use |fail| rather than |assertEquals| because it allows us to give a clearer message.
+        if (!expectedFields.equals(actualFields)) {
+            String message = String.format(
+                    "Tracepoint format given by \"%s\" does not match the expected format.\n"
+                            + "Expected fields:\n%s\n\nActual fields:\n%s\n\n",
+                    GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH, expectedFields, actualFields);
+            fail(message);
+        }
+    }
+}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 469c9e6..ec58325 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -72,7 +72,6 @@
         "libstatssocket",
         "libutils",
         "libui",
-        "lib-platform-compat-native-api",
         "server_configurable_flags",
     ],
     static_libs: [
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 19cad7b..6c4b11e 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -29,8 +29,6 @@
 #endif
 #include <unordered_set>
 
-#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
-
 #define INDENT1 "  "
 #define INDENT2 "    "
 #define INDENT3 "      "
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index a6baf2f..e000283 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -33,7 +33,6 @@
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
-#include <utils/Vector.h>
 
 using android::os::BnInputFlinger;
 
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 902bd0d..75071d5 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -28,7 +28,6 @@
         "libstatslog",
         "libui",
         "libutils",
-        "lib-platform-compat-native-api",
     ],
     static_libs: [
         "libattestation",
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 4757d31..cdad9c9 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -66,7 +66,6 @@
         "libui",
         "libgui",
         "libutils",
-        "lib-platform-compat-native-api",
         "server_configurable_flags",
     ],
     static_libs: [
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f190f67..441a1de 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -24,8 +24,6 @@
 #include <android-base/stringprintf.h>
 #include <android/os/IInputConstants.h>
 #include <binder/Binder.h>
-#include <binder/IServiceManager.h>
-#include <com/android/internal/compat/IPlatformCompatNative.h>
 #include <ftl/enum.h>
 #include <gui/SurfaceComposerClient.h>
 #include <input/InputDevice.h>
@@ -63,7 +61,6 @@
 using android::os::IInputConstants;
 using android::os::InputEventInjectionResult;
 using android::os::InputEventInjectionSync;
-using com::android::internal::compat::IPlatformCompatNative;
 
 namespace android::inputdispatcher {
 
@@ -411,15 +408,6 @@
     return *lhs == *rhs;
 }
 
-sp<IPlatformCompatNative> getCompatService() {
-    sp<IBinder> service(defaultServiceManager()->getService(String16("platform_compat_native")));
-    if (service == nullptr) {
-        ALOGE("Failed to link to compat service");
-        return nullptr;
-    }
-    return interface_cast<IPlatformCompatNative>(service);
-}
-
 KeyEvent createKeyEvent(const KeyEntry& entry) {
     KeyEvent event;
     event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
@@ -570,8 +558,7 @@
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
         mLatencyAggregator(),
-        mLatencyTracker(&mLatencyAggregator),
-        mCompatService(getCompatService()) {
+        mLatencyTracker(&mLatencyAggregator) {
     mLooper = new Looper(false);
     mReporter = createInputReporter();
 
@@ -4812,18 +4799,6 @@
                 ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
             }
             oldWindowHandle->releaseChannel();
-            // To avoid making too many calls into the compat framework, only
-            // check for window flags when windows are going away.
-            // TODO(b/157929241) : delete this. This is only needed temporarily
-            // in order to gather some data about the flag usage
-            if (oldWindowHandle->getInfo()->flags.test(WindowInfo::Flag::SLIPPERY)) {
-                ALOGW("%s has FLAG_SLIPPERY. Please report this in b/157929241",
-                      oldWindowHandle->getName().c_str());
-                if (mCompatService != nullptr) {
-                    mCompatService->reportChangeByUid(IInputConstants::BLOCK_FLAG_SLIPPERY,
-                                                      oldWindowHandle->getInfo()->ownerUid);
-                }
-            }
         }
     }
 }
@@ -5670,6 +5645,15 @@
             return;
         }
 
+        if (enabled) {
+            if (std::find(mIneligibleDisplaysForPointerCapture.begin(),
+                          mIneligibleDisplaysForPointerCapture.end(),
+                          mFocusedDisplayId) != mIneligibleDisplaysForPointerCapture.end()) {
+                ALOGW("Ignoring request to enable Pointer Capture: display is not eligible");
+                return;
+            }
+        }
+
         setPointerCaptureLocked(enabled);
     } // release lock
 
@@ -5677,6 +5661,16 @@
     mLooper->wake();
 }
 
+void InputDispatcher::setDisplayEligibilityForPointerCapture(int32_t displayId, bool isEligible) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        std::erase(mIneligibleDisplaysForPointerCapture, displayId);
+        if (!isEligible) {
+            mIneligibleDisplaysForPointerCapture.push_back(displayId);
+        }
+    } // release lock
+}
+
 std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
         const sp<IBinder>& token) {
     for (const auto& it : mGestureMonitorsByDisplay) {
@@ -6336,6 +6330,8 @@
         // Call focus resolver to clean up stale requests. This must be called after input windows
         // have been removed for the removed display.
         mFocusResolver.displayRemoved(displayId);
+        // Reset pointer capture eligibility, regardless of previous state.
+        std::erase(mIneligibleDisplaysForPointerCapture, displayId);
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ee50ec5..7564839 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -36,7 +36,6 @@
 #include "TouchedWindow.h"
 
 #include <attestation/HmacKeyManager.h>
-#include <com/android/internal/compat/IPlatformCompatNative.h>
 #include <gui/InputApplication.h>
 #include <gui/WindowInfo.h>
 #include <input/Input.h>
@@ -137,6 +136,7 @@
     status_t pilferPointers(const sp<IBinder>& token) override;
     void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
     bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
+    void setDisplayEligibilityForPointerCapture(int displayId, bool isEligible) override;
 
     std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
 
@@ -421,6 +421,10 @@
     // This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
     sp<IBinder> mWindowTokenWithPointerCapture GUARDED_BY(mLock);
 
+    // Displays that are ineligible for pointer capture.
+    // TODO(b/214621487): Remove or move to a display flag.
+    std::vector<int32_t> mIneligibleDisplaysForPointerCapture GUARDED_BY(mLock);
+
     // Disable Pointer Capture as a result of loss of window focus.
     void disablePointerCaptureForcedLocked() REQUIRES(mLock);
 
@@ -679,7 +683,6 @@
     void traceWaitQueueLength(const Connection& connection);
 
     sp<InputReporterInterface> mReporter;
-    sp<com::android::internal::compat::IPlatformCompatNative> mCompatService;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index c469ec3..16a6f16 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -201,6 +201,13 @@
      */
     virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) = 0;
 
+    /**
+     * Sets the eligibility of a given display to enable pointer capture. If a display is marked
+     * ineligible, all attempts to request pointer capture for windows on that display will fail.
+     *  TODO(b/214621487): Remove or move to a display flag.
+     */
+    virtual void setDisplayEligibilityForPointerCapture(int displayId, bool isEligible) = 0;
+
     /* Flush input device motion sensor.
      *
      * Returns true on success.
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 1aab856..41ecef3 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -17,6 +17,7 @@
 #ifndef _UI_INPUT_READER_BASE_H
 #define _UI_INPUT_READER_BASE_H
 
+#include <android/os/IInputConstants.h>
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
@@ -87,6 +88,8 @@
     virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
             int32_t sw) = 0;
 
+    virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
+
     /* Toggle Caps Lock */
     virtual void toggleCapsLockState(int32_t deviceId) = 0;
 
@@ -286,7 +289,10 @@
 
     InputReaderConfiguration()
           : virtualKeyQuietTime(0),
-            pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
+            pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
+                                             static_cast<float>(
+                                                     android::os::IInputConstants::
+                                                             DEFAULT_POINTER_ACCELERATION)),
             wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
             pointerGesturesEnabled(true),
             pointerGestureQuietInterval(100 * 1000000LL),            // 100 ms
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 269bdab..db67877 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -880,6 +880,42 @@
     return AKEY_STATE_UNKNOWN;
 }
 
+int32_t EventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
+    std::scoped_lock _l(mLock);
+
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr || !device->hasValidFd() || device->keyMap.keyCharacterMap == nullptr ||
+        device->keyMap.keyLayoutMap == nullptr) {
+        return AKEYCODE_UNKNOWN;
+    }
+    std::vector<int32_t> scanCodes;
+    device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode, &scanCodes);
+    if (scanCodes.empty()) {
+        ALOGW("Failed to get key code for key location: no scan code maps to key code %d for input"
+              "device %d",
+              locationKeyCode, deviceId);
+        return AKEYCODE_UNKNOWN;
+    }
+    if (scanCodes.size() > 1) {
+        ALOGW("Multiple scan codes map to the same key code %d, returning only the first match",
+              locationKeyCode);
+    }
+    int32_t outKeyCode;
+    status_t mapKeyRes =
+            device->getKeyCharacterMap()->mapKey(scanCodes[0], 0 /*usageCode*/, &outKeyCode);
+    switch (mapKeyRes) {
+        case OK:
+            return outKeyCode;
+        case NAME_NOT_FOUND:
+            // key character map doesn't re-map this scanCode, hence the keyCode remains the same
+            return locationKeyCode;
+        default:
+            ALOGW("Failed to get key code for key location: Key character map returned error %s",
+                  statusToString(mapKeyRes).c_str());
+            return AKEYCODE_UNKNOWN;
+    }
+}
+
 int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
     if (sw >= 0 && sw <= SW_MAX) {
         std::scoped_lock _l(mLock);
@@ -1262,12 +1298,11 @@
 bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) {
     std::scoped_lock _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) {
-        device->keyMap.keyCharacterMap->combine(*map);
-        device->keyMap.keyCharacterMapFile = device->keyMap.keyCharacterMap->getLoadFileName();
-        return true;
+    if (device == nullptr || map == nullptr || device->keyMap.keyCharacterMap == nullptr) {
+        return false;
     }
-    return false;
+    device->keyMap.keyCharacterMap->combine(*map);
+    return true;
 }
 
 static std::string generateDescriptor(InputDeviceIdentifier& identifier) {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 7cff672..3e95fa9 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -475,6 +475,23 @@
     return result;
 }
 
+int32_t InputDevice::getKeyCodeForKeyLocation(int32_t locationKeyCode) const {
+    std::optional<int32_t> result = first_in_mappers<int32_t>(
+            [locationKeyCode](const InputMapper& mapper) -> std::optional<int32_t> const {
+                if (sourcesMatchMask(mapper.getSources(), AINPUT_SOURCE_KEYBOARD)) {
+                    return std::make_optional(mapper.getKeyCodeForKeyLocation(locationKeyCode));
+                }
+                return std::nullopt;
+            });
+    if (!result) {
+        ALOGE("Failed to get key code for key location: No matching InputMapper with source mask "
+              "KEYBOARD found. The provided input device with id %d has sources %s.",
+              getId(), inputEventSourceToString(getSources()).c_str());
+        return AKEYCODE_UNKNOWN;
+    }
+    return *result;
+}
+
 void InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {
     for_each_mapper([sequence, repeat, token](InputMapper& mapper) {
         mapper.vibrate(sequence, repeat, token);
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 564db56..31d331b 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -303,7 +303,7 @@
     device->process(rawEvents, count);
 }
 
-InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) {
+InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) const {
     auto deviceIt =
             std::find_if(mDevices.begin(), mDevices.end(), [deviceId](const auto& devicePair) {
                 return devicePair.second->getId() == deviceId;
@@ -589,6 +589,18 @@
     return result;
 }
 
+int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device == nullptr) {
+        ALOGW("Failed to get key code for key location: Input device with id %d not found",
+              deviceId);
+        return AKEYCODE_UNKNOWN;
+    }
+    return device->getKeyCodeForKeyLocation(locationKeyCode);
+}
+
 void InputReader::requestRefreshConfiguration(uint32_t changes) {
     std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 1f96294..18e912d 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -306,6 +306,7 @@
     virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
     virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
                                           int32_t* outValue) const = 0;
+    virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
 
     /*
      * Examine key input devices for specific framework keycode support
@@ -482,6 +483,8 @@
     int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final;
     int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override final;
     int32_t getSwitchState(int32_t deviceId, int32_t sw) const override final;
+    int32_t getKeyCodeForKeyLocation(int32_t deviceId,
+                                     int32_t locationKeyCode) const override final;
     status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
                                   int32_t* outValue) const override final;
 
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 518aaa0..11c074a 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -84,6 +84,7 @@
     int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
     int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
     int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+    int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const;
     bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
                                uint8_t* outFlags);
     void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
@@ -216,7 +217,8 @@
     // return the first value returned by a function over every mapper.
     // if all mappers return nullopt, return nullopt.
     template <typename T>
-    inline std::optional<T> first_in_mappers(std::function<std::optional<T>(InputMapper&)> f) {
+    inline std::optional<T> first_in_mappers(
+            std::function<std::optional<T>(InputMapper&)> f) const {
         for (auto& deviceEntry : mDevices) {
             auto& devicePair = deviceEntry.second;
             auto& mappers = devicePair.second;
@@ -312,6 +314,9 @@
     inline int32_t getKeyCodeState(int32_t keyCode) const {
         return mEventHub->getKeyCodeState(mId, keyCode);
     }
+    inline int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const {
+        return mEventHub->getKeyCodeForKeyLocation(mId, locationKeyCode);
+    }
     inline int32_t getSwitchState(int32_t sw) const { return mEventHub->getSwitchState(mId, sw); }
     inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const {
         return mEventHub->getAbsoluteAxisValue(mId, code, outValue);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index fb1d166..daeaa1d 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -69,6 +69,8 @@
     int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
     int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
 
+    int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
+
     void toggleCapsLockState(int32_t deviceId) override;
 
     bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
@@ -239,7 +241,7 @@
                                      const int32_t* keyCodes, uint8_t* outFlags) REQUIRES(mLock);
 
     // find an InputDevice from an InputDevice id
-    InputDevice* findInputDeviceLocked(int32_t deviceId) REQUIRES(mLock);
+    InputDevice* findInputDeviceLocked(int32_t deviceId) const REQUIRES(mLock);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index fcb56ef..dc5fcec 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -66,7 +66,7 @@
 
 CursorInputMapper::~CursorInputMapper() {}
 
-uint32_t CursorInputMapper::getSources() {
+uint32_t CursorInputMapper::getSources() const {
     return mSource;
 }
 
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 9a8ca01..c84c6c4 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -56,7 +56,7 @@
     explicit CursorInputMapper(InputDeviceContext& deviceContext);
     virtual ~CursorInputMapper();
 
-    virtual uint32_t getSources() override;
+    virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
     virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 37d1d74..6b5d37f 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -26,7 +26,7 @@
 ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext)
       : InputMapper(deviceContext) {}
 
-uint32_t ExternalStylusInputMapper::getSources() {
+uint32_t ExternalStylusInputMapper::getSources() const {
     return AINPUT_SOURCE_STYLUS;
 }
 
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 1d42b30..516aa51 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -30,7 +30,7 @@
     explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext);
     virtual ~ExternalStylusInputMapper() = default;
 
-    virtual uint32_t getSources() override;
+    virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
     virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index b9aef54..7b185e0 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -51,6 +51,10 @@
     return AKEY_STATE_UNKNOWN;
 }
 
+int32_t InputMapper::getKeyCodeForKeyLocation(int32_t locationKeyCode) const {
+    return AKEYCODE_UNKNOWN;
+}
+
 bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                         const int32_t* keyCodes, uint8_t* outFlags) {
     return false;
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index d83d74d..fce6409 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -45,12 +45,13 @@
 
     inline int32_t getDeviceId() { return mDeviceContext.getId(); }
     inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+    inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; };
     inline const std::string getDeviceName() const { return mDeviceContext.getName(); }
     inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
     inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
     inline InputListenerInterface& getListener() { return getContext()->getListener(); }
 
-    virtual uint32_t getSources() = 0;
+    virtual uint32_t getSources() const = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(std::string& dump);
     virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
@@ -61,6 +62,8 @@
     virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
     virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+    virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const;
+
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                        const int32_t* keyCodes, uint8_t* outFlags);
     virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index ad11b05..a8e9bae 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -25,7 +25,7 @@
 
 JoystickInputMapper::~JoystickInputMapper() {}
 
-uint32_t JoystickInputMapper::getSources() {
+uint32_t JoystickInputMapper::getSources() const {
     return AINPUT_SOURCE_JOYSTICK;
 }
 
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index bba95ad..307bf5b 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -26,7 +26,7 @@
     explicit JoystickInputMapper(InputDeviceContext& deviceContext);
     virtual ~JoystickInputMapper();
 
-    virtual uint32_t getSources() override;
+    virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
     virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index a8602a4..1d63c0e 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -95,7 +95,7 @@
 
 KeyboardInputMapper::~KeyboardInputMapper() {}
 
-uint32_t KeyboardInputMapper::getSources() {
+uint32_t KeyboardInputMapper::getSources() const {
     return mSource;
 }
 
@@ -375,6 +375,10 @@
     return getDeviceContext().getScanCodeState(scanCode);
 }
 
+int32_t KeyboardInputMapper::getKeyCodeForKeyLocation(int32_t locationKeyCode) const {
+    return getDeviceContext().getKeyCodeForKeyLocation(locationKeyCode);
+}
+
 bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                                 const int32_t* keyCodes, uint8_t* outFlags) {
     return getDeviceContext().markSupportedKeyCodes(numCodes, keyCodes, outFlags);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index fc92320..3787696 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -26,7 +26,7 @@
     KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
     virtual ~KeyboardInputMapper();
 
-    virtual uint32_t getSources() override;
+    virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
     virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
@@ -38,6 +38,7 @@
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                        const int32_t* keyCodes, uint8_t* outFlags) override;
+    virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
 
     virtual int32_t getMetaState() override;
     virtual bool updateMetaState(int32_t keyCode) override;
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index b83a8fc..eca25f6 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -31,7 +31,7 @@
 
 RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
 
-uint32_t RotaryEncoderInputMapper::getSources() {
+uint32_t RotaryEncoderInputMapper::getSources() const {
     return mSource;
 }
 
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index e0c9404..1859355 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -27,7 +27,7 @@
     explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext);
     virtual ~RotaryEncoderInputMapper();
 
-    virtual uint32_t getSources() override;
+    virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
     virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 677a372..b01c2bc 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -57,7 +57,7 @@
 
 SensorInputMapper::~SensorInputMapper() {}
 
-uint32_t SensorInputMapper::getSources() {
+uint32_t SensorInputMapper::getSources() const {
     return AINPUT_SOURCE_SENSOR;
 }
 
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 1797fe3..27a6177 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -28,7 +28,7 @@
     explicit SensorInputMapper(InputDeviceContext& deviceContext);
     ~SensorInputMapper() override;
 
-    uint32_t getSources() override;
+    uint32_t getSources() const override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
     void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 3237824..ebb5de6 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -25,7 +25,7 @@
 
 SwitchInputMapper::~SwitchInputMapper() {}
 
-uint32_t SwitchInputMapper::getSources() {
+uint32_t SwitchInputMapper::getSources() const {
     return AINPUT_SOURCE_SWITCH;
 }
 
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index 4d74163..64b9aa2 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -26,7 +26,7 @@
     explicit SwitchInputMapper(InputDeviceContext& deviceContext);
     virtual ~SwitchInputMapper();
 
-    virtual uint32_t getSources() override;
+    virtual uint32_t getSources() const override;
     virtual void process(const RawEvent* rawEvent) override;
 
     virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 0a5de27..4772beb 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -178,7 +178,7 @@
 
 TouchInputMapper::~TouchInputMapper() {}
 
-uint32_t TouchInputMapper::getSources() {
+uint32_t TouchInputMapper::getSources() const {
     return mSource;
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 9fd30e4..c948f56 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -138,7 +138,7 @@
     explicit TouchInputMapper(InputDeviceContext& deviceContext);
     ~TouchInputMapper() override;
 
-    uint32_t getSources() override;
+    uint32_t getSources() const override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
     void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 1976fed..33db527 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -25,7 +25,7 @@
 
 VibratorInputMapper::~VibratorInputMapper() {}
 
-uint32_t VibratorInputMapper::getSources() {
+uint32_t VibratorInputMapper::getSources() const {
     return 0;
 }
 
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 7ce621a..d3c22b6 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -26,7 +26,7 @@
     explicit VibratorInputMapper(InputDeviceContext& deviceContext);
     virtual ~VibratorInputMapper();
 
-    virtual uint32_t getSources() override;
+    virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void process(const RawEvent* rawEvent) override;
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 068e03c..e2596f0 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -439,6 +439,8 @@
         KeyedVector<int32_t, KeyInfo> keysByScanCode;
         KeyedVector<int32_t, KeyInfo> keysByUsageCode;
         KeyedVector<int32_t, bool> leds;
+        // fake mapping which would normally come from keyCharacterMap
+        std::unordered_map<int32_t, int32_t> keyCodeMapping;
         std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
         BitArray<MSC_MAX> mscBitmask;
         std::vector<VirtualKeyDefinition> virtualKeys;
@@ -600,6 +602,11 @@
         }
     }
 
+    void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) {
+        Device* device = getDevice(deviceId);
+        device->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
+    }
+
     void addLed(int32_t deviceId, int32_t led, bool initialState) {
         Device* device = getDevice(deviceId);
         device->leds.add(led, initialState);
@@ -863,6 +870,15 @@
         return -1;
     }
 
+    int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
+        Device* device = getDevice(deviceId);
+        if (!device) {
+            return AKEYCODE_UNKNOWN;
+        }
+        auto it = device->keyCodeMapping.find(locationKeyCode);
+        return it != device->keyCodeMapping.end() ? it->second : locationKeyCode;
+    }
+
     // Return true if the device has non-empty key layout.
     bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
                                uint8_t* outFlags) const override {
@@ -1034,6 +1050,8 @@
     KeyedVector<int32_t, int32_t> mKeyCodeStates;
     KeyedVector<int32_t, int32_t> mScanCodeStates;
     KeyedVector<int32_t, int32_t> mSwitchStates;
+    // fake mapping which would normally come from keyCharacterMap
+    std::unordered_map<int32_t, int32_t> mKeyCodeMapping;
     std::vector<int32_t> mSupportedKeyCodes;
 
     std::mutex mLock;
@@ -1122,8 +1140,12 @@
         mSupportedKeyCodes.push_back(keyCode);
     }
 
+    void addKeyCodeMapping(int32_t fromKeyCode, int32_t toKeyCode) {
+        mKeyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
+    }
+
 private:
-    uint32_t getSources() override { return mSources; }
+    uint32_t getSources() const override { return mSources; }
 
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {
         InputMapper::populateDeviceInfo(deviceInfo);
@@ -1164,6 +1186,11 @@
         return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
     }
 
+    int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override {
+        auto it = mKeyCodeMapping.find(locationKeyCode);
+        return it != mKeyCodeMapping.end() ? it->second : locationKeyCode;
+    }
+
     int32_t getScanCodeState(uint32_t, int32_t scanCode) override {
         ssize_t index = mScanCodeStates.indexOfKey(scanCode);
         return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
@@ -1712,6 +1739,37 @@
             << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
 }
 
+TEST_F(InputReaderTest, GetKeyCodeForKeyLocation_ForwardsRequestsToMappers) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr int32_t eventHubId = 1;
+    FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "keyboard",
+                                                           InputDeviceClass::KEYBOARD,
+                                                           AINPUT_SOURCE_KEYBOARD, nullptr);
+    mapper.addKeyCodeMapping(AKEYCODE_Y, AKEYCODE_Z);
+
+    ASSERT_EQ(AKEYCODE_UNKNOWN, mReader->getKeyCodeForKeyLocation(0, AKEYCODE_Y))
+            << "Should return unknown when the device with the specified id is not found.";
+
+    ASSERT_EQ(AKEYCODE_Z, mReader->getKeyCodeForKeyLocation(deviceId, AKEYCODE_Y))
+            << "Should return correct mapping when device id is valid and mapping exists.";
+
+    ASSERT_EQ(AKEYCODE_A, mReader->getKeyCodeForKeyLocation(deviceId, AKEYCODE_A))
+            << "Should return the location key code when device id is valid and there's no "
+               "mapping.";
+}
+
+TEST_F(InputReaderTest, GetKeyCodeForKeyLocation_NoKeyboardMapper) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr int32_t eventHubId = 1;
+    FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "joystick",
+                                                           InputDeviceClass::JOYSTICK,
+                                                           AINPUT_SOURCE_GAMEPAD, nullptr);
+    mapper.addKeyCodeMapping(AKEYCODE_Y, AKEYCODE_Z);
+
+    ASSERT_EQ(AKEYCODE_UNKNOWN, mReader->getKeyCodeForKeyLocation(deviceId, AKEYCODE_Y))
+            << "Should return unknown when the device id is valid but there is no keyboard mapper";
+}
+
 TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
@@ -2330,6 +2388,17 @@
         mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
+    void assertReceivedMotion(int32_t action, const std::vector<Point>& points) {
+        NotifyMotionArgs args;
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+        EXPECT_EQ(action, args.action);
+        ASSERT_EQ(points.size(), args.pointerCount);
+        for (size_t i = 0; i < args.pointerCount; i++) {
+            EXPECT_EQ(points[i].x, args.pointerCoords[i].getX());
+            EXPECT_EQ(points[i].y, args.pointerCoords[i].getY());
+        }
+    }
+
     std::unique_ptr<UinputTouchScreen> mDevice;
 };
 
@@ -2340,16 +2409,19 @@
     // ACTION_DOWN
     mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
     // ACTION_MOVE
     mDevice->sendMove(centerPoint + Point(1, 1));
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
     // ACTION_UP
     mDevice->sendUp();
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
@@ -2362,6 +2434,7 @@
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
@@ -2369,27 +2442,129 @@
     const Point secondPoint = centerPoint + Point(100, 100);
     mDevice->sendSlot(SECOND_SLOT);
     mDevice->sendTrackingId(SECOND_TRACKING_ID);
-    mDevice->sendDown(secondPoint + Point(1, 1));
+    mDevice->sendDown(secondPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
 
     // ACTION_MOVE (Second slot)
-    mDevice->sendMove(secondPoint);
+    mDevice->sendMove(secondPoint + Point(1, 1));
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
     // ACTION_POINTER_UP (Second slot)
     mDevice->sendPointerUp();
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ACTION_POINTER_1_UP, args.action);
 
     // ACTION_UP
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendUp();
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
 
+/**
+ * What happens when a pointer goes up while another pointer moves in the same frame? Are POINTER_UP
+ * events guaranteed to contain the same data as a preceding MOVE, or can they contain different
+ * data?
+ * In this test, we try to send a change in coordinates in Pointer 0 in the same frame as the
+ * liftoff of Pointer 1. We check that POINTER_UP event is generated first, and the MOVE event
+ * for Pointer 0 only is generated after.
+ * Suppose we are only interested in learning the movement of Pointer 0. If we only observe MOVE
+ * events, we will not miss any information.
+ * Even though the Pointer 1 up event contains updated Pointer 0 coordinates, there is another MOVE
+ * event generated afterwards that contains the newest movement of pointer 0.
+ * This is important for palm rejection. If there is a subsequent InputListener stage that detects
+ * palms, and wants to cancel Pointer 1, then it is safe to simply drop POINTER_1_UP event without
+ * losing information about non-palm pointers.
+ */
+TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    assertReceivedMotion(AMOTION_EVENT_ACTION_DOWN, {centerPoint});
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint);
+    mDevice->sendSync();
+    assertReceivedMotion(ACTION_POINTER_1_DOWN, {centerPoint, secondPoint});
+
+    // ACTION_MOVE (First slot)
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendMove(centerPoint + Point(5, 5));
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendPointerUp();
+    // Send a single sync for the above 2 pointer updates
+    mDevice->sendSync();
+
+    // First, we should get POINTER_UP for the second pointer
+    assertReceivedMotion(ACTION_POINTER_1_UP,
+                         {/*first pointer */ centerPoint + Point(5, 5),
+                          /*second pointer*/ secondPoint});
+
+    // Next, the MOVE event for the first pointer
+    assertReceivedMotion(AMOTION_EVENT_ACTION_MOVE, {centerPoint + Point(5, 5)});
+}
+
+/**
+ * Similar scenario as above. The difference is that when the second pointer goes up, it will first
+ * move, and then it will go up, all in the same frame.
+ * In this scenario, the movement of the second pointer just prior to liftoff is ignored, and never
+ * gets sent to the listener.
+ */
+TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    assertReceivedMotion(AMOTION_EVENT_ACTION_DOWN, {centerPoint});
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint);
+    mDevice->sendSync();
+    assertReceivedMotion(ACTION_POINTER_1_DOWN, {centerPoint, secondPoint});
+
+    // ACTION_MOVE (First slot)
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendMove(centerPoint + Point(5, 5));
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendMove(secondPoint + Point(6, 6));
+    mDevice->sendPointerUp();
+    // Send a single sync for the above 2 pointer updates
+    mDevice->sendSync();
+
+    // First, we should get POINTER_UP for the second pointer
+    // The movement of the second pointer during the liftoff frame is ignored.
+    // The coordinates 'secondPoint + Point(6, 6)' are never sent to the listener.
+    assertReceivedMotion(ACTION_POINTER_1_UP,
+                         {/*first pointer */ centerPoint + Point(5, 5),
+                          /*second pointer*/ secondPoint});
+
+    // Next, the MOVE event for the first pointer
+    assertReceivedMotion(AMOTION_EVENT_ACTION_MOVE, {centerPoint + Point(5, 5)});
+}
+
 TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
     NotifyMotionArgs args;
     const Point centerPoint = mDevice->getCenterPoint();
@@ -2398,6 +2573,7 @@
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
@@ -2406,11 +2582,13 @@
     mDevice->sendSlot(SECOND_SLOT);
     mDevice->sendTrackingId(SECOND_TRACKING_ID);
     mDevice->sendDown(secondPoint);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
 
     // ACTION_MOVE (second slot)
     mDevice->sendMove(secondPoint + Point(1, 1));
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
@@ -2418,18 +2596,21 @@
     // a palm event.
     // Expect to receive the ACTION_POINTER_UP with cancel flag.
     mDevice->sendToolType(MT_TOOL_PALM);
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ACTION_POINTER_1_UP, args.action);
     ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, args.flags);
 
     // Send up to second slot, expect first slot send moving.
     mDevice->sendPointerUp();
+    mDevice->sendSync();
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
 
     // Send ACTION_UP (first slot)
     mDevice->sendSlot(FIRST_SLOT);
     mDevice->sendUp();
+    mDevice->sendSync();
 
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
@@ -3486,6 +3667,19 @@
     ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 }
 
+TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) {
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+    mFakeEventHub->addKeyCodeMapping(EVENTHUB_ID, AKEYCODE_Y, AKEYCODE_Z);
+    ASSERT_EQ(AKEYCODE_Z, mapper.getKeyCodeForKeyLocation(AKEYCODE_Y))
+            << "If a mapping is available, the result is equal to the mapping";
+
+    ASSERT_EQ(AKEYCODE_A, mapper.getKeyCodeForKeyLocation(AKEYCODE_A))
+            << "If no mapping is available, the result is the key location";
+}
+
 TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 7fec2c8..132b877 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -170,28 +170,27 @@
     injectEvent(EV_KEY, BTN_TOUCH, 1);
     injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x);
     injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
-    injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
 void UinputTouchScreen::sendMove(const Point& point) {
     injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x);
     injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
-    injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
 void UinputTouchScreen::sendPointerUp() {
     sendTrackingId(0xffffffff);
-    injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
 void UinputTouchScreen::sendUp() {
     sendTrackingId(0xffffffff);
     injectEvent(EV_KEY, BTN_TOUCH, 0);
-    injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
 void UinputTouchScreen::sendToolType(int32_t toolType) {
     injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType);
+}
+
+void UinputTouchScreen::sendSync() {
     injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 01a557c..a37fc2b 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -142,6 +142,7 @@
     void sendPointerUp();
     void sendUp();
     void sendToolType(int32_t toolType);
+    void sendSync();
 
     const Point getCenterPoint();
 
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index 74c47ba..cdd95ca 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -214,13 +214,39 @@
             dstInfo->type = (int32_t)srcInfo.type;
             dstInfo->serial = srcInfo.serial;
 
-            // TODO(b/195593357): Finish additional info conversion
-            // CHECK_EQ(sizeof(srcInfo.payload.values), sizeof(dstInfo->data_int32));
+            switch (srcInfo.payload.getTag()) {
+                case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: {
+                    const auto &values =
+                            srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>()
+                                    .values;
+                    CHECK_EQ(values.size() * sizeof(int32_t), sizeof(dstInfo->data_int32));
+                    memcpy(dstInfo->data_int32, values.data(), sizeof(dstInfo->data_int32));
+                    break;
+                }
+                case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: {
+                    const auto &values =
+                            srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>()
+                                    .values;
+                    CHECK_EQ(values.size() * sizeof(float), sizeof(dstInfo->data_float));
+                    memcpy(dstInfo->data_float, values.data(), sizeof(dstInfo->data_float));
+                    break;
+                }
+                default: {
+                    ALOGE("Invalid sensor additional info tag: %d", srcInfo.payload.getTag());
+                }
+            }
+            break;
+        }
 
-            // memcpy(dstInfo->data_int32,
-            //        &srcInfo.u,
-            //        sizeof(dstInfo->data_int32));
-
+        case SensorType::HEAD_TRACKER: {
+            const auto &ht = src.payload.get<Event::EventPayload::headTracker>();
+            dst->head_tracker.rx = ht.rx;
+            dst->head_tracker.ry = ht.ry;
+            dst->head_tracker.rz = ht.rz;
+            dst->head_tracker.vx = ht.vx;
+            dst->head_tracker.vy = ht.vy;
+            dst->head_tracker.vz = ht.vz;
+            dst->head_tracker.discontinuity_count = ht.discontinuityCount;
             break;
         }
 
@@ -238,6 +264,7 @@
     *dst = {
             .timestamp = src.timestamp,
             .sensorHandle = src.sensor,
+            .sensorType = (SensorType) src.type,
     };
 
     switch (dst->sensorType) {
@@ -359,12 +386,29 @@
             info.type = (AdditionalInfo::AdditionalInfoType)srcInfo.type;
             info.serial = srcInfo.serial;
 
-            // TODO(b/195593357): Finish additional info conversion
+            AdditionalInfo::AdditionalInfoPayload::Int32Values data;
+            CHECK_EQ(data.values.size() * sizeof(int32_t), sizeof(srcInfo.data_int32));
+            memcpy(data.values.data(), srcInfo.data_int32, sizeof(srcInfo.data_int32));
+            info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(data);
 
             dst->payload.set<Event::EventPayload::Tag::additional>(info);
             break;
         }
 
+        case SensorType::HEAD_TRACKER: {
+            Event::EventPayload::HeadTracker headTracker;
+            headTracker.rx = src.head_tracker.rx;
+            headTracker.ry = src.head_tracker.ry;
+            headTracker.rz = src.head_tracker.rz;
+            headTracker.vx = src.head_tracker.vx;
+            headTracker.vy = src.head_tracker.vy;
+            headTracker.vz = src.head_tracker.vz;
+            headTracker.discontinuityCount = src.head_tracker.discontinuity_count;
+
+            dst->payload.set<Event::EventPayload::Tag::headTracker>(headTracker);
+            break;
+        }
+
         default: {
             CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
 
@@ -445,7 +489,6 @@
 
         ndk::SpAIBinder binder(AServiceManager_waitForService(aidlServiceName.c_str()));
         if (binder.get() != nullptr) {
-
             mSensors = ISensors::fromBinder(binder);
             mEventQueue = std::make_unique<AidlMessageQueue<
                     Event, SynchronizedReadWrite>>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 9bc7b8e..517d383 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1254,6 +1254,11 @@
     for (auto &sensor : sensorList) {
         int32_t id = getIdFromUuid(sensor.getUuid());
         sensor.setId(id);
+        // The sensor UUID must always be anonymized here for non privileged clients.
+        // There is no other checks after this point before returning to client process.
+        if (!isAudioServerOrSystemServerUid(IPCThreadState::self()->getCallingUid())) {
+            sensor.anonymizeUuid();
+        }
     }
 }
 
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 9b6d01a..b009829 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -26,6 +26,7 @@
 #include <binder/IUidObserver.h>
 #include <cutils/compiler.h>
 #include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
 #include <sensor/ISensorServer.h>
 #include <sensor/ISensorEventConnection.h>
 #include <sensor/Sensor.h>
@@ -447,6 +448,10 @@
     // Removes the capped rate on active direct connections (when the mic toggle is flipped to off)
     void uncapRates(userid_t userId);
 
+    static inline bool isAudioServerOrSystemServerUid(uid_t uid) {
+        return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER;
+    }
+
     static uint8_t sHmacGlobalKey[128];
     static bool sHmacGlobalKeyIsValid;
 
diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index fdd56b3..baa01c9 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -58,6 +58,9 @@
         case SENSOR_TYPE_HINGE_ANGLE:
             return 1;
 
+        case SENSOR_TYPE_HEAD_TRACKER:
+            return 7;
+
         default:
             return 3;
     }
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5560ed7..af0f524 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -125,10 +125,7 @@
         thin: true,
     },
     whole_program_vtables: true, // Requires ThinLTO
-    pgo: {
-        sampling: true,
-        profile_file: "surfaceflinger/surfaceflinger.profdata",
-    },
+    afdo: true,
     // TODO(b/131771163): Fix broken fuzzer support with LTO.
     sanitize: {
         fuzzer: false,
@@ -158,9 +155,7 @@
         "DisplayHardware/AidlComposerHal.cpp",
         "DisplayHardware/HidlComposerHal.cpp",
         "DisplayHardware/ComposerHal.cpp",
-        "DisplayHardware/DisplayIdentification.cpp",
         "DisplayHardware/FramebufferSurface.cpp",
-        "DisplayHardware/Hash.cpp",
         "DisplayHardware/HWC2.cpp",
         "DisplayHardware/HWComposer.cpp",
         "DisplayHardware/PowerAdvisor.cpp",
@@ -186,14 +181,13 @@
         "RenderArea.cpp",
         "Scheduler/DispSyncSource.cpp",
         "Scheduler/EventThread.cpp",
+        "Scheduler/FrameRateOverrideMappings.cpp",
         "Scheduler/OneShotTimer.cpp",
         "Scheduler/LayerHistory.cpp",
         "Scheduler/LayerInfo.cpp",
         "Scheduler/MessageQueue.cpp",
         "Scheduler/RefreshRateConfigs.cpp",
         "Scheduler/Scheduler.cpp",
-        "Scheduler/SchedulerUtils.cpp",
-        "Scheduler/Timer.cpp",
         "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncPredictor.cpp",
         "Scheduler/VSyncReactor.cpp",
@@ -231,7 +225,6 @@
         "libcutils",
         "libdisplayservicehidl",
         "libhidlbase",
-        "liblayers_proto",
         "liblog",
         "libprocessgroup",
         "libsync",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d61a4cb..e797b5d 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -164,7 +164,7 @@
     const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
             ((isSecure() || isProtected()) && !targetSettings.isSecure);
     const bool bufferCanBeUsedAsHwTexture =
-            mBufferInfo.mBuffer->getBuffer()->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+            mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
     compositionengine::LayerFE::LayerSettings& layer = *result;
     if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
         ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
@@ -201,7 +201,7 @@
     }
     layer.source.buffer.maxLuminanceNits = maxLuminance;
     layer.frameNumber = mCurrentFrameNumber;
-    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()->getId() : 0;
+    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
 
     const bool useFiltering =
             targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
@@ -436,7 +436,7 @@
 
 void BufferLayer::gatherBufferInfo() {
     mBufferInfo.mPixelFormat =
-            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getBuffer()->format;
+            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
     mBufferInfo.mFrameLatencyNeeded = true;
 }
 
@@ -533,10 +533,10 @@
     }
 
     if (oldBufferInfo.mBuffer != nullptr) {
-        uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
-        uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
-        if (bufWidth != uint32_t(oldBufferInfo.mBuffer->getBuffer()->width) ||
-            bufHeight != uint32_t(oldBufferInfo.mBuffer->getBuffer()->height)) {
+        uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+        uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+        if (bufWidth != oldBufferInfo.mBuffer->getWidth() ||
+            bufHeight != oldBufferInfo.mBuffer->getHeight()) {
             recomputeVisibleRegions = true;
         }
     }
@@ -558,7 +558,7 @@
 
 bool BufferLayer::isProtected() const {
     return (mBufferInfo.mBuffer != nullptr) &&
-            (mBufferInfo.mBuffer->getBuffer()->getUsage() & GRALLOC_USAGE_PROTECTED);
+            (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
 }
 
 // As documented in libhardware header, formats in the range
@@ -638,8 +638,8 @@
         return Rect::INVALID_RECT;
     }
 
-    uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
 
     // Undo any transformations on the buffer and return the result.
     if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -670,8 +670,8 @@
         return parentBounds;
     }
 
-    uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
 
     // Undo any transformations on the buffer and return the result.
     if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -713,7 +713,7 @@
         return mBufferInfo.mCrop;
     } else if (mBufferInfo.mBuffer != nullptr) {
         // otherwise we use the whole buffer
-        return mBufferInfo.mBuffer->getBuffer()->getBounds();
+        return mBufferInfo.mBuffer->getBounds();
     } else {
         // if we don't have a buffer yet, we use an empty/invalid crop
         return Rect();
@@ -820,6 +820,10 @@
     return isFixedSize();
 }
 
+const std::shared_ptr<renderengine::ExternalTexture>& BufferLayer::getExternalTexture() const {
+    return mBufferInfo.mBuffer;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 34d11ac..99267be 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -111,6 +111,7 @@
     ui::Dataspace getDataSpace() const override;
 
     sp<GraphicBuffer> getBuffer() const override;
+    const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const override;
 
     ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index c79fa11..9ae45fc 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -41,6 +41,7 @@
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
 #include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
@@ -208,8 +209,9 @@
         if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr ||
             mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) {
             mImages[item->mSlot] = std::make_shared<
-                    renderengine::ExternalTexture>(item->mGraphicBuffer, mRE,
-                                                   renderengine::ExternalTexture::Usage::READABLE);
+                    renderengine::impl::ExternalTexture>(item->mGraphicBuffer, mRE,
+                                                         renderengine::impl::ExternalTexture::
+                                                                 Usage::READABLE);
         }
     }
 
@@ -462,8 +464,9 @@
         if (oldImage == nullptr || oldImage->getBuffer() == nullptr ||
             oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) {
             mImages[item.mSlot] = std::make_shared<
-                    renderengine::ExternalTexture>(item.mGraphicBuffer, mRE,
-                                                   renderengine::ExternalTexture::Usage::READABLE);
+                    renderengine::impl::ExternalTexture>(item.mGraphicBuffer, mRE,
+                                                         renderengine::impl::ExternalTexture::
+                                                                 Usage::READABLE);
         }
     }
 }
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 2fac880..40fc342 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -295,8 +295,8 @@
         return assignTransform(&mDrawingState.transform, t);
     }
 
-    uint32_t bufferWidth = mDrawingState.buffer->getBuffer()->getWidth();
-    uint32_t bufferHeight = mDrawingState.buffer->getBuffer()->getHeight();
+    uint32_t bufferWidth = mDrawingState.buffer->getWidth();
+    uint32_t bufferHeight = mDrawingState.buffer->getHeight();
     // Undo any transformations on the buffer.
     if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
         std::swap(bufferWidth, bufferHeight);
@@ -316,8 +316,7 @@
     return assignTransform(&mDrawingState.transform, t);
 }
 
-bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
-                                 bool allowNonRectPreservingTransforms) {
+bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix) {
     if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
         mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
         return false;
@@ -326,12 +325,6 @@
     ui::Transform t;
     t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
 
-    if (!allowNonRectPreservingTransforms && !t.preserveRects()) {
-        ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER nor "
-              "ROTATE_SURFACE_FLINGER ignored");
-        return false;
-    }
-
     mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
 
     mDrawingState.sequence++;
@@ -368,46 +361,13 @@
     return true;
 }
 
-std::shared_ptr<renderengine::ExternalTexture> BufferStateLayer::getBufferFromBufferData(
-        const BufferData& bufferData) {
-    bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
-    bool bufferSizeExceedsLimit = false;
-    std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
-    if (cacheIdChanged && bufferData.buffer != nullptr) {
-        bufferSizeExceedsLimit =
-                mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
-                                                     bufferData.buffer->getHeight());
-        if (!bufferSizeExceedsLimit) {
-            ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
-            buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
-        }
-    } else if (cacheIdChanged) {
-        buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
-    } else if (bufferData.buffer != nullptr) {
-        bufferSizeExceedsLimit =
-                mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
-                                                     bufferData.buffer->getHeight());
-        if (!bufferSizeExceedsLimit) {
-            buffer = std::make_shared<
-                    renderengine::ExternalTexture>(bufferData.buffer, mFlinger->getRenderEngine(),
-                                                   renderengine::ExternalTexture::Usage::READABLE);
-        }
-    }
-    ALOGE_IF(bufferSizeExceedsLimit,
-             "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
-             "limit.",
-             getDebugName());
-    return buffer;
-}
-
-bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime,
+bool BufferStateLayer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                                 const BufferData& bufferData, nsecs_t postTime,
                                  nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  std::optional<nsecs_t> dequeueTime,
                                  const FrameTimelineInfo& info) {
     ATRACE_CALL();
 
-    const std::shared_ptr<renderengine::ExternalTexture>& buffer =
-            getBufferFromBufferData(bufferData);
     if (!buffer) {
         return false;
     }
@@ -419,8 +379,9 @@
 
     if (mDrawingState.buffer) {
         mReleasePreviousBuffer = true;
-        if (mDrawingState.buffer != mBufferInfo.mBuffer ||
-            mDrawingState.frameNumber != mBufferInfo.mFrameNumber) {
+        if (!mBufferInfo.mBuffer ||
+            (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
+             mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) {
             // If mDrawingState has a buffer, and we are about to update again
             // before swapping to drawing state, then the first buffer will be
             // dropped and we should decrement the pending buffer count and
@@ -448,7 +409,7 @@
 
     mDrawingState.frameNumber = frameNumber;
     mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
-    mDrawingState.buffer = buffer;
+    mDrawingState.buffer = std::move(buffer);
     mDrawingState.clientCacheId = bufferData.cachedBuffer;
 
     mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
@@ -484,8 +445,8 @@
 
     setFrameTimelineVsyncForBufferTransaction(info, postTime);
 
-    if (buffer && dequeueTime && *dequeueTime != 0) {
-        const uint64_t bufferId = buffer->getBuffer()->getId();
+    if (dequeueTime && *dequeueTime != 0) {
+        const uint64_t bufferId = mDrawingState.buffer->getId();
         mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
         mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
                                                FrameTracer::FrameEvent::DEQUEUE);
@@ -493,8 +454,8 @@
                                                FrameTracer::FrameEvent::QUEUE);
     }
 
-    mDrawingState.width = mDrawingState.buffer->getBuffer()->getWidth();
-    mDrawingState.height = mDrawingState.buffer->getBuffer()->getHeight();
+    mDrawingState.width = mDrawingState.buffer->getWidth();
+    mDrawingState.height = mDrawingState.buffer->getHeight();
     mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
     return true;
 }
@@ -599,8 +560,8 @@
         return Rect::INVALID_RECT;
     }
 
-    uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
 
     // Undo any transformations on the buffer and return the result.
     if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -709,7 +670,7 @@
     }
 
     const int32_t layerId = getSequence();
-    const uint64_t bufferId = mDrawingState.buffer->getBuffer()->getId();
+    const uint64_t bufferId = mDrawingState.buffer->getId();
     const uint64_t frameNumber = mDrawingState.frameNumber;
     const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
     mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
@@ -749,7 +710,7 @@
         return BAD_VALUE;
     }
 
-    if (!mBufferInfo.mBuffer || s.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) {
+    if (!mBufferInfo.mBuffer || !s.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
         decrementPendingBufferCount();
     }
 
@@ -874,10 +835,10 @@
 Rect BufferStateLayer::computeBufferCrop(const State& s) {
     if (s.buffer && !s.bufferCrop.isEmpty()) {
         Rect bufferCrop;
-        s.buffer->getBuffer()->getBounds().intersect(s.bufferCrop, &bufferCrop);
+        s.buffer->getBounds().intersect(s.bufferCrop, &bufferCrop);
         return bufferCrop;
     } else if (s.buffer) {
-        return s.buffer->getBuffer()->getBounds();
+        return s.buffer->getBounds();
     } else {
         return s.bufferCrop;
     }
@@ -898,8 +859,8 @@
         return false;
     }
 
-    int32_t bufferWidth = s.buffer->getBuffer()->width;
-    int32_t bufferHeight = s.buffer->getBuffer()->height;
+    int32_t bufferWidth = static_cast<int32_t>(s.buffer->getWidth());
+    int32_t bufferHeight = static_cast<int32_t>(s.buffer->getHeight());
 
     // Undo any transformations on the buffer and return the result.
     if (s.bufferTransform & ui::Transform::ROT_90) {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index eea700c..248e013 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -57,7 +57,8 @@
     bool setTransform(uint32_t transform) override;
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
-    bool setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
+    bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
+                   const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
                    bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
                    const FrameTimelineInfo& info) override;
     bool setDataspace(ui::Dataspace dataspace) override;
@@ -69,8 +70,7 @@
     bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
                        nsecs_t requestedPresentTime) override;
     bool setPosition(float /*x*/, float /*y*/) override;
-    bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
-                   bool /*allowNonRectPreservingTransforms*/);
+    bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/);
 
     // Override to ignore legacy layer state properties that are not used by BufferStateLayer
     bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
@@ -136,9 +136,6 @@
 
     bool bufferNeedsFiltering() const override;
 
-    std::shared_ptr<renderengine::ExternalTexture> getBufferFromBufferData(
-            const BufferData& bufferData);
-
     ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index e7b8995..3c7b9d9 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -22,6 +22,7 @@
 #include <cinttypes>
 
 #include <android-base/stringprintf.h>
+#include <renderengine/impl/ExternalTexture.h>
 
 #include "ClientCache.h"
 
@@ -109,8 +110,9 @@
                         "Attempted to build the ClientCache before a RenderEngine instance was "
                         "ready!");
     processBuffers[id].buffer = std::make_shared<
-            renderengine::ExternalTexture>(buffer, *mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE);
+            renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
+                                                 renderengine::impl::ExternalTexture::Usage::
+                                                         READABLE);
     return true;
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 01dd534..16cb41b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -19,7 +19,7 @@
 #include <cstdint>
 #include <optional>
 
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
 
 #include <compositionengine/Output.h>
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 73770b7..d8644a4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -34,7 +34,7 @@
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
 
 namespace android {
 
@@ -173,6 +173,8 @@
     // Sets the projection state to use
     virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                                const Rect& orientedDisplaySpaceRect) = 0;
+    // Sets the brightness that will take effect next frame.
+    virtual void setNextBrightness(float brightness) = 0;
     // Sets the bounds to use
     virtual void setDisplaySize(const ui::Size&) = 0;
     // Gets the transform hint used in layers that belong to this output. Used to guide
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index a2824f5..bf5184e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -28,8 +28,8 @@
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
 
+#include <ui/DisplayIdentification.h>
 #include "DisplayHardware/ComposerHal.h"
-#include "DisplayHardware/DisplayIdentification.h"
 
 #include "LayerFE.h"
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 3571e11..b777241 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -26,7 +26,8 @@
 #include <ui/PixelFormat.h>
 #include <ui/Size.h>
 
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
+
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/PowerAdvisor.h"
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 844876a..a7a8e97 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -45,6 +45,7 @@
     void setLayerCachingTexturePoolEnabled(bool) override;
     void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                        const Rect& orientedDisplaySpaceRect) override;
+    void setNextBrightness(float brightness) override;
     void setDisplaySize(const ui::Size&) override;
     void setLayerFilter(ui::LayerFilter) override;
     ui::Transform::RotationFlags getTransformHint() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index c8f177b..cc7c257 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -133,6 +133,10 @@
     // White point of the client target
     float clientTargetWhitePointNits{-1.f};
 
+    // Display brightness that will take effect this frame.
+    // This is slightly distinct from nits, in that nits cannot be passed to hw composer.
+    std::optional<float> displayBrightness = std::nullopt;
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 0082dac..d64d676 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -25,7 +25,7 @@
 #include <ui/FloatRect.h>
 #include <ui/Rect.h>
 
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 08a8b84..6fb3e08 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -22,8 +22,7 @@
 #include <compositionengine/mock/Output.h>
 #include <gmock/gmock.h>
 #include <system/window.h>
-
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
 
 namespace android::compositionengine::mock {
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 7b0d028..b68b95d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -39,6 +39,7 @@
     MOCK_METHOD1(setLayerCachingEnabled, void(bool));
     MOCK_METHOD1(setLayerCachingTexturePoolEnabled, void(bool));
     MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
+    MOCK_METHOD1(setNextBrightness, void(float));
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
     MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags());
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 08dd22d..186e191 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -226,6 +226,17 @@
     // Get any composition changes requested by the HWC device, and apply them.
     std::optional<android::HWComposer::DeviceRequestedChanges> changes;
     auto& hwc = getCompositionEngine().getHwComposer();
+    if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
+        physicalDisplayId && getState().displayBrightness) {
+        const status_t result =
+                hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness,
+                                         Hwc2::Composer::DisplayBrightnessOptions{
+                                                 .applyImmediately = false})
+                        .get();
+        ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
+                 getName().c_str(), result, strerror(-result));
+    }
+
     if (status_t result =
                 hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
                                                 getState().earliestPresentTime,
@@ -248,6 +259,8 @@
     auto& state = editState();
     state.usesClientComposition = anyLayersRequireClientComposition();
     state.usesDeviceComposition = !allLayersRequireClientComposition();
+    // Clear out the display brightness now that it's been communicated to composer.
+    state.displayBrightness.reset();
 }
 
 bool Display::getSkipColorTransform() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 6833584..162d84e 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -197,6 +197,10 @@
     dirtyEntireOutput();
 }
 
+void Output::setNextBrightness(float brightness) {
+    editState().displayBrightness = brightness;
+}
+
 void Output::setDisplaySize(const ui::Size& size) {
     mRenderSurface->setDisplaySize(size);
 
@@ -1055,9 +1059,11 @@
 
     // If we have a valid current display brightness use that, otherwise fall back to the
     // display's max desired
-    clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f
+    clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f
             ? outputState.displayBrightnessNits
             : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+    clientCompositionDisplay.maxLuminance =
+            mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
     clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits;
 
     // Compute the global color transform matrix.
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 545e2a2..95cc5a8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -481,7 +481,7 @@
     if (auto error = hwcLayer->setVisibleRegion(visibleRegion); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(),
               to_string(error).c_str(), static_cast<int32_t>(error));
-        outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
+        visibleRegion.dump(LOG_TAG);
     }
 
     const auto dataspace = outputDependentState.overrideInfo.buffer
@@ -551,10 +551,10 @@
 
 void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
                                             const LayerFECompositionState& outputIndependentState) {
-    hal::Color color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
-                        static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
-                        static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
-                        255};
+    aidl::android::hardware::graphics::composer3::Color color = {outputIndependentState.color.r,
+                                                                 outputIndependentState.color.g,
+                                                                 outputIndependentState.color.b,
+                                                                 1.0f};
 
     if (auto error = hwcLayer->setColor(color); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set color: %s (%d)", getLayerFE().getDebugName(),
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index a19d23f..12c2c8e 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -28,6 +28,7 @@
 #include <log/log.h>
 #include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <system/window.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
@@ -182,9 +183,10 @@
         mTexture = texture;
     } else {
         mTexture = std::make_shared<
-                renderengine::ExternalTexture>(GraphicBuffer::from(buffer),
-                                               mCompositionEngine.getRenderEngine(),
-                                               renderengine::ExternalTexture::Usage::WRITEABLE);
+                renderengine::impl::ExternalTexture>(GraphicBuffer::from(buffer),
+                                                     mCompositionEngine.getRenderEngine(),
+                                                     renderengine::impl::ExternalTexture::Usage::
+                                                             WRITEABLE);
     }
     mTextureCache.push_back(mTexture);
     if (mTextureCache.size() > mMaxTextureCacheSize) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
index 497c433..54ecb56 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -20,6 +20,7 @@
 #define LOG_TAG "Planner"
 
 #include <compositionengine/impl/planner/TexturePool.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <utils/Log.h>
 
 namespace android::compositionengine::impl::planner {
@@ -82,16 +83,19 @@
 std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() {
     LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size");
     return std::make_shared<
-            renderengine::ExternalTexture>(sp<GraphicBuffer>::
-                                                   make(mSize.getWidth(), mSize.getHeight(),
-                                                        HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                                        GraphicBuffer::USAGE_HW_RENDER |
-                                                                GraphicBuffer::USAGE_HW_COMPOSER |
-                                                                GraphicBuffer::USAGE_HW_TEXTURE,
-                                                        "Planner"),
-                                           mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(sp<GraphicBuffer>::
+                                             make(static_cast<uint32_t>(mSize.getWidth()),
+                                                  static_cast<uint32_t>(mSize.getHeight()),
+                                                  HAL_PIXEL_FORMAT_RGBA_8888, 1U,
+                                                  static_cast<uint64_t>(
+                                                          GraphicBuffer::USAGE_HW_RENDER |
+                                                          GraphicBuffer::USAGE_HW_COMPOSER |
+                                                          GraphicBuffer::USAGE_HW_TEXTURE),
+                                                  "Planner"),
+                                     mRenderEngine,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
 }
 
 void TexturePool::setEnabled(bool enabled) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 8558a80..7dd4c21 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -37,6 +37,7 @@
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
 #include "MockPowerAdvisor.h"
+#include "ftl/future.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
@@ -48,6 +49,7 @@
 namespace hal = android::hardware::graphics::composer::hal;
 
 using testing::_;
+using testing::ByMove;
 using testing::DoAll;
 using testing::Eq;
 using testing::InSequence;
@@ -594,6 +596,38 @@
     EXPECT_TRUE(state.usesDeviceComposition);
 }
 
+TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithDisplayBrightness) {
+    // Since two calls are made to anyLayersRequireClientComposition with different return
+    // values, use a Sequence to control the matching so the values are returned in a known
+    // order.
+    constexpr float kDisplayBrightness = 0.5f;
+    Sequence s;
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(true));
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer,
+                setDisplayBrightness(DEFAULT_DISPLAY_ID, kDisplayBrightness,
+                                     Hwc2::Composer::DisplayBrightnessOptions{.applyImmediately =
+                                                                                      false}))
+            .WillOnce(Return(ByMove(ftl::yield<status_t>(NO_ERROR))));
+
+    EXPECT_CALL(mHwComposer,
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+
+    mDisplay->setNextBrightness(kDisplayBrightness);
+    mDisplay->chooseCompositionStrategy();
+
+    auto& state = mDisplay->getState();
+    EXPECT_FALSE(state.usesClientComposition);
+    EXPECT_TRUE(state.usesDeviceComposition);
+    EXPECT_FALSE(state.displayBrightness.has_value());
+}
+
 TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
     android::HWComposer::DeviceRequestedChanges changes{
             {{nullptr, Composition::CLIENT}},
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index ff68053..5185ea9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -58,7 +58,7 @@
                        const android::sp<android::Fence>&));
     MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
     MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
-    MOCK_METHOD1(setColor, Error(hal::Color));
+    MOCK_METHOD1(setColor, Error(aidl::android::hardware::graphics::composer3::Color));
     MOCK_METHOD1(setCompositionType,
                  Error(aidl::android::hardware::graphics::composer3::Composition));
     MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
@@ -75,6 +75,7 @@
     MOCK_METHOD3(setLayerGenericMetadata,
                  Error(const std::string&, bool, const std::vector<uint8_t>&));
     MOCK_METHOD1(setWhitePointNits, Error(float));
+    MOCK_METHOD1(setBlockingRegion, Error(const android::Region&));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index a590e2a..bd3022b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -83,7 +83,9 @@
     MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
     MOCK_METHOD4(getDisplayedContentSample,
                  status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
-    MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(PhysicalDisplayId, float));
+    MOCK_METHOD3(setDisplayBrightness,
+                 std::future<status_t>(PhysicalDisplayId, float,
+                                       const Hwc2::Composer::DisplayBrightnessOptions&));
     MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
 
     MOCK_METHOD2(onHotplug,
@@ -104,6 +106,9 @@
                  status_t(PhysicalDisplayId, hal::HWConfigId,
                           const hal::VsyncPeriodChangeConstraints&,
                           hal::VsyncPeriodChangeTimeline*));
+    MOCK_METHOD2(setBootDisplayMode, status_t(PhysicalDisplayId, hal::HWConfigId));
+    MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId));
+    MOCK_METHOD1(getPreferredBootDisplayMode, hal::HWConfigId(PhysicalDisplayId));
     MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
     MOCK_METHOD2(getSupportedContentTypes,
                  status_t(PhysicalDisplayId, std::vector<hal::ContentType>*));
@@ -122,6 +127,7 @@
                 (const, override));
     MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
                 (const, override));
+    MOCK_METHOD(bool, getBootDisplayModeSupport, (), (override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index db4151b..50adcfb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -39,11 +39,10 @@
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
     MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
-    MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
-                (override));
     MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
                 (override));
     MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
+    MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index ad7976f..132ac02 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -24,6 +24,7 @@
 #include <gtest/gtest.h>
 #include <log/log.h>
 
+#include <renderengine/impl/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/PixelFormat.h>
 #include "MockHWC2.h"
@@ -815,10 +816,11 @@
         auto& overrideInfo = mOutputLayer.editState().overrideInfo;
 
         overrideInfo.buffer = std::make_shared<
-                renderengine::ExternalTexture>(kOverrideBuffer, mRenderEngine,
-                                               renderengine::ExternalTexture::Usage::READABLE |
-                                                       renderengine::ExternalTexture::Usage::
-                                                               WRITEABLE);
+                renderengine::impl::ExternalTexture>(kOverrideBuffer, mRenderEngine,
+                                                     renderengine::impl::ExternalTexture::Usage::
+                                                                     READABLE |
+                                                             renderengine::impl::ExternalTexture::
+                                                                     Usage::WRITEABLE);
         overrideInfo.acquireFence = kOverrideFence;
         overrideInfo.displayFrame = kOverrideDisplayFrame;
         overrideInfo.dataspace = kOverrideDataspace;
@@ -864,9 +866,8 @@
     }
 
     void expectSetColorCall() {
-        const hal::Color color = {static_cast<uint8_t>(std::round(kColor.r * 255)),
-                                  static_cast<uint8_t>(std::round(kColor.g * 255)),
-                                  static_cast<uint8_t>(std::round(kColor.b * 255)), 255};
+        const aidl::android::hardware::graphics::composer3::Color color = {kColor.r, kColor.g,
+                                                                           kColor.b, 1.0f};
 
         EXPECT_CALL(*mHwcLayer, setColor(ColorEq(color))).WillOnce(Return(kError));
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 6d96260..655db4b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -26,6 +26,8 @@
 #include <compositionengine/mock/RenderSurface.h>
 #include <ftl/future.h>
 #include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -37,7 +39,6 @@
 #include "MockHWC2.h"
 #include "RegionMatcher.h"
 #include "TestUtils.h"
-#include "renderengine/ExternalTexture.h"
 
 namespace android::compositionengine {
 namespace {
@@ -273,9 +274,10 @@
     // Inject some layers
     InjectedLayer layer;
     layer.outputLayerState.overrideInfo.buffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(new GraphicBuffer(), renderEngine,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     injectOutputLayer(layer);
     // inject a null layer to check for null exceptions
     injectNullOutputLayer();
@@ -574,6 +576,17 @@
     EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.getBoundsAsRect());
 }
 
+/**
+ * Output::setDisplayBrightness()
+ */
+
+TEST_F(OutputTest, setNextBrightness) {
+    constexpr float kDisplayBrightness = 0.5f;
+    mOutput->setNextBrightness(kDisplayBrightness);
+    ASSERT_TRUE(mOutput->getState().displayBrightness.has_value());
+    EXPECT_EQ(kDisplayBrightness, mOutput->getState().displayBrightness);
+}
+
 /*
  * Output::getDirtyRegion()
  */
@@ -956,9 +969,10 @@
     mOutput->planComposition();
 
     std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(new GraphicBuffer(), renderEngine,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     layer1.outputLayerState.overrideInfo.buffer = buffer;
     layer2.outputLayerState.overrideInfo.buffer = buffer;
     layer1.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer;
@@ -3058,6 +3072,8 @@
     static constexpr float kDefaultMaxLuminance = 0.9f;
     static constexpr float kDefaultAvgLuminance = 0.7f;
     static constexpr float kDefaultMinLuminance = 0.1f;
+    static constexpr float kUnknownLuminance = -1.f;
+    static constexpr float kDisplayLuminance = 80.f;
     static constexpr float kClientTargetLuminanceNits = 200.f;
 
     static const Rect kDefaultOutputFrame;
@@ -3077,9 +3093,10 @@
     mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
     StrictMock<OutputPartialMock> mOutput;
     std::shared_ptr<renderengine::ExternalTexture> mOutputBuffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
 
     std::optional<base::unique_fd> mReadyFence;
 };
@@ -3090,6 +3107,7 @@
 const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
 const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
 const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
+
 const HdrCapabilities OutputComposeSurfacesTest::
         kHdrCapabilities{{},
                          OutputComposeSurfacesTest::kDefaultMaxLuminance,
@@ -3313,9 +3331,10 @@
             .WillRepeatedly(Return());
 
     const auto otherOutputBuffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
             .WillOnce(Return(mOutputBuffer))
             .WillOnce(Return(otherOutputBuffer));
@@ -3392,6 +3411,14 @@
         auto andIfUsesHdr(bool used) {
             EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasWideColorGamut())
                     .WillOnce(Return(used));
+            return nextState<OutputWithDisplayBrightnessNits>();
+        }
+    };
+
+    struct OutputWithDisplayBrightnessNits
+          : public CallOrderStateMachineHelper<TestType, OutputWithDisplayBrightnessNits> {
+        auto withDisplayBrightnessNits(float nits) {
+            getInstance()->mOutput.mState.displayBrightnessNits = nits;
             return nextState<SkipColorTransformState>();
         }
     };
@@ -3423,11 +3450,34 @@
 TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) {
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(true)
+            .withDisplayBrightnessNits(kUnknownLuminance)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            kDefaultOutputOrientationFlags,
-                                            kClientTargetLuminanceNits})
+            .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip,
+                                            .clip = kDefaultOutputViewport,
+                                            .maxLuminance = kDefaultMaxLuminance,
+                                            .currentLuminanceNits = kDefaultMaxLuminance,
+                                            .outputDataspace = kDefaultOutputDataspace,
+                                            .colorTransform = mat4(),
+                                            .orientation = kDefaultOutputOrientationFlags,
+                                            .targetLuminanceNits = kClientTargetLuminanceNits})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings,
+       forHdrMixedCompositionWithDisplayBrightness) {
+    verify().ifMixedCompositionIs(true)
+            .andIfUsesHdr(true)
+            .withDisplayBrightnessNits(kDisplayLuminance)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip,
+                                            .clip = kDefaultOutputViewport,
+                                            .maxLuminance = kDefaultMaxLuminance,
+                                            .currentLuminanceNits = kDisplayLuminance,
+                                            .outputDataspace = kDefaultOutputDataspace,
+                                            .colorTransform = mat4(),
+                                            .orientation = kDefaultOutputOrientationFlags,
+                                            .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3435,11 +3485,16 @@
 TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) {
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(false)
+            .withDisplayBrightnessNits(kUnknownLuminance)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            kDefaultOutputOrientationFlags,
-                                            kClientTargetLuminanceNits})
+            .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip,
+                                            .clip = kDefaultOutputViewport,
+                                            .maxLuminance = kDefaultMaxLuminance,
+                                            .currentLuminanceNits = kDefaultMaxLuminance,
+                                            .outputDataspace = kDefaultOutputDataspace,
+                                            .colorTransform = mat4(),
+                                            .orientation = kDefaultOutputOrientationFlags,
+                                            .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3447,11 +3502,16 @@
 TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) {
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
+            .withDisplayBrightnessNits(kUnknownLuminance)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed(
-                    {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
-                     kDefaultOutputDataspace, kDefaultColorTransformMat,
-                     kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
+            .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip,
+                                            .clip = kDefaultOutputViewport,
+                                            .maxLuminance = kDefaultMaxLuminance,
+                                            .currentLuminanceNits = kDefaultMaxLuminance,
+                                            .outputDataspace = kDefaultOutputDataspace,
+                                            .colorTransform = kDefaultColorTransformMat,
+                                            .orientation = kDefaultOutputOrientationFlags,
+                                            .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3459,11 +3519,16 @@
 TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) {
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(false)
+            .withDisplayBrightnessNits(kUnknownLuminance)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed(
-                    {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
-                     kDefaultOutputDataspace, kDefaultColorTransformMat,
-                     kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
+            .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip,
+                                            .clip = kDefaultOutputViewport,
+                                            .maxLuminance = kDefaultMaxLuminance,
+                                            .currentLuminanceNits = kDefaultMaxLuminance,
+                                            .outputDataspace = kDefaultOutputDataspace,
+                                            .colorTransform = kDefaultColorTransformMat,
+                                            .orientation = kDefaultOutputOrientationFlags,
+                                            .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3472,11 +3537,16 @@
        usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) {
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
+            .withDisplayBrightnessNits(kUnknownLuminance)
             .andIfSkipColorTransform(true)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            kDefaultOutputOrientationFlags,
-                                            kClientTargetLuminanceNits})
+            .thenExpectDisplaySettingsUsed({.physicalDisplay = kDefaultOutputDestinationClip,
+                                            .clip = kDefaultOutputViewport,
+                                            .maxLuminance = kDefaultMaxLuminance,
+                                            .currentLuminanceNits = kDefaultMaxLuminance,
+                                            .outputDataspace = kDefaultOutputDataspace,
+                                            .colorTransform = mat4(),
+                                            .orientation = kDefaultOutputOrientationFlags,
+                                            .targetLuminanceNits = kClientTargetLuminanceNits})
             .execute()
             .expectAFenceWasReturned();
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 7c8e41b..e5f9ebf 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -27,6 +27,7 @@
 #include <compositionengine/mock/OutputLayer.h>
 #include <gtest/gtest.h>
 #include <renderengine/ExternalTexture.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/GraphicBuffer.h>
 
@@ -251,9 +252,10 @@
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
     const auto buffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE |
-                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+            renderengine::impl::
+                    ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                     renderengine::impl::ExternalTexture::Usage::READABLE |
+                                             renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
@@ -269,8 +271,8 @@
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
-    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
-                                                                        mRenderEngine, false);
+    const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(),
+                                                                              mRenderEngine, false);
     mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
@@ -288,8 +290,8 @@
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
-    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
-                                                                        mRenderEngine, false);
+    const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(),
+                                                                              mRenderEngine, false);
     mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
@@ -327,8 +329,8 @@
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
-    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
-                                                                        mRenderEngine, false);
+    const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(),
+                                                                              mRenderEngine, false);
     mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index d5a117a..4ae921d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -22,6 +22,7 @@
 #include <gmock/gmock-actions.h>
 #include <gtest/gtest.h>
 #include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/FakeExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/GraphicTypes.h>
 #include <utils/Errors.h>
@@ -702,9 +703,11 @@
     std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
     clientCompList3.push_back({});
 
-    clientCompList3[0].source.buffer.buffer = std::make_shared<
-            renderengine::ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
-                                           renderengine::ExternalTexture::READABLE);
+    clientCompList3[0].source.buffer.buffer =
+            std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                                      1ULL /* bufferId */,
+                                                                      HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                      0ULL /*usage*/);
 
     EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
@@ -901,9 +904,11 @@
     std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
     clientCompList3.push_back({});
 
-    clientCompList3[0].source.buffer.buffer = std::make_shared<
-            renderengine::ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
-                                           renderengine::ExternalTexture::READABLE);
+    clientCompList3[0].source.buffer.buffer =
+            std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                                      1ULL /* bufferId */,
+                                                                      HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                      0ULL /*usage*/);
 
     EXPECT_CALL(*layerFE1,
                 prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 35d051e..58dc244 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -23,6 +23,7 @@
 #include <gtest/gtest.h>
 #include <renderengine/ExternalTexture.h>
 #include <renderengine/LayerSettings.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <chrono>
 
@@ -639,9 +640,10 @@
             LayerFE::LayerSettings{},
     };
     clientCompositionList[0].source.buffer.buffer = std::make_shared<
-            renderengine::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer,
-                                           mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE);
+            renderengine::impl::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer,
+                                                 mRenderEngine,
+                                                 renderengine::impl::ExternalTexture::Usage::
+                                                         READABLE);
     EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientCompositionList(_))
             .WillOnce(Return(clientCompositionList));
 
@@ -711,9 +713,10 @@
             LayerFE::LayerSettings{},
     };
     clientCompositionList[0].source.buffer.buffer = std::make_shared<
-            renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
-                                           mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE);
+            renderengine::impl::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
+                                                 mRenderEngine,
+                                                 renderengine::impl::ExternalTexture::Usage::
+                                                         READABLE);
     EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
             .WillOnce(Return(clientCompositionList));
 
@@ -781,9 +784,10 @@
             LayerFE::LayerSettings{},
     };
     clientCompositionList[0].source.buffer.buffer = std::make_shared<
-            renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
-                                           mRenderEngine,
-                                           renderengine::ExternalTexture::Usage::READABLE);
+            renderengine::impl::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
+                                                 mRenderEngine,
+                                                 renderengine::impl::ExternalTexture::Usage::
+                                                         READABLE);
     EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
             .WillOnce(Return(clientCompositionList));
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 76bbe2c..a36ea72 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -311,6 +311,22 @@
                                            orientedDisplaySpaceRect);
 }
 
+void DisplayDevice::stageBrightness(float brightness) {
+    mStagedBrightness = brightness;
+}
+
+void DisplayDevice::persistBrightness(bool needsComposite) {
+    if (needsComposite && mStagedBrightness && mBrightness != *mStagedBrightness) {
+        getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+        mBrightness = *mStagedBrightness;
+    }
+    mStagedBrightness = std::nullopt;
+}
+
+std::optional<float> DisplayDevice::getStagedBrightness() const {
+    return mStagedBrightness;
+}
+
 ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
     return sPrimaryDisplayRotationFlags;
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 324145e..0c9063d 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -41,7 +41,7 @@
 
 #include "MainThreadGuard.h"
 
-#include "DisplayHardware/DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/Hal.h"
 #include "DisplayHardware/PowerAdvisor.h"
@@ -99,6 +99,9 @@
     void setLayerStack(ui::LayerStack);
     void setDisplaySize(int width, int height);
     void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
+    void stageBrightness(float brightness) REQUIRES(SF_MAIN_THREAD);
+    void persistBrightness(bool needsComposite) REQUIRES(SF_MAIN_THREAD);
+    bool isBrightnessStale() const REQUIRES(SF_MAIN_THREAD);
     void setFlags(uint32_t flags);
 
     ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
@@ -106,6 +109,7 @@
 
     static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
 
+    std::optional<float> getStagedBrightness() const REQUIRES(SF_MAIN_THREAD);
     ui::Transform::RotationFlags getTransformHint() const;
     const ui::Transform& getTransform() const;
     const Rect& getLayerStackSpaceRect() const;
@@ -271,6 +275,8 @@
     hardware::graphics::composer::hal::PowerMode mPowerMode =
             hardware::graphics::composer::hal::PowerMode::OFF;
     DisplayModePtr mActiveMode;
+    std::optional<float> mStagedBrightness = std::nullopt;
+    float mBrightness = -1.f;
     const DisplayModes mSupportedModes;
 
     std::atomic<nsecs_t> mLastHwVsync = 0;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 1091a75..1448e56 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -110,16 +110,6 @@
 }
 
 template <>
-Color translate(IComposerClient::Color x) {
-    return Color{
-            .r = static_cast<int8_t>(x.r),
-            .g = static_cast<int8_t>(x.g),
-            .b = static_cast<int8_t>(x.b),
-            .a = static_cast<int8_t>(x.a),
-    };
-}
-
-template <>
 AidlPerFrameMetadataBlob translate(IComposerClient::PerFrameMetadataBlob x) {
     AidlPerFrameMetadataBlob blob;
     blob.key = translate<AidlPerFrameMetadataKey>(x.key),
@@ -246,6 +236,8 @@
     switch (feature) {
         case OptionalFeature::RefreshRateSwitching:
         case OptionalFeature::ExpectedPresentTime:
+        case OptionalFeature::DisplayBrightnessCommand:
+        case OptionalFeature::BootDisplayConfig:
             return true;
     }
 }
@@ -669,10 +661,8 @@
     return Error::NONE;
 }
 
-Error AidlComposer::setLayerColor(Display display, Layer layer,
-                                  const IComposerClient::Color& color) {
-    mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer),
-                          translate<Color>(color));
+Error AidlComposer::setLayerColor(Display display, Layer layer, const Color& color) {
+    mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color);
     return Error::NONE;
 }
 
@@ -906,13 +896,14 @@
     return Error::NONE;
 }
 
-Error AidlComposer::setDisplayBrightness(Display display, float brightness) {
-    const auto status =
-            mAidlComposerClient->setDisplayBrightness(translate<int64_t>(display), brightness);
-    if (!status.isOk()) {
-        ALOGE("setDisplayBrightness failed %s", status.getDescription().c_str());
-        return static_cast<Error>(status.getServiceSpecificError());
+Error AidlComposer::setDisplayBrightness(Display display, float brightness,
+                                         const DisplayBrightnessOptions& options) {
+    mWriter.setDisplayBrightness(translate<int64_t>(display), brightness);
+
+    if (options.applyImmediately) {
+        return execute();
     }
+
     return Error::NONE;
 }
 
@@ -1019,6 +1010,38 @@
     return V2_4::Error::UNSUPPORTED;
 }
 
+Error AidlComposer::setBootDisplayConfig(Display display, Config config) {
+    const auto status = mAidlComposerClient->setBootDisplayConfig(translate<int64_t>(display),
+                                                                  translate<int32_t>(config));
+    if (!status.isOk()) {
+        ALOGE("setBootDisplayConfig failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::clearBootDisplayConfig(Display display) {
+    const auto status = mAidlComposerClient->clearBootDisplayConfig(translate<int64_t>(display));
+    if (!status.isOk()) {
+        ALOGE("clearBootDisplayConfig failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getPreferredBootDisplayConfig(Display display, Config* config) {
+    int32_t displayConfig;
+    const auto status =
+            mAidlComposerClient->getPreferredBootDisplayConfig(translate<int64_t>(display),
+                                                               &displayConfig);
+    if (!status.isOk()) {
+        ALOGE("getPreferredBootDisplayConfig failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    *config = translate<uint32_t>(displayConfig);
+    return Error::NONE;
+}
+
 Error AidlComposer::getClientTargetProperty(
         Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
         float* whitePointNits) {
@@ -1035,5 +1058,11 @@
     return Error::NONE;
 }
 
+Error AidlComposer::setLayerBlockingRegion(Display display, Layer layer,
+                                           const std::vector<IComposerClient::Rect>& blocking) {
+    mWriter.setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer),
+                                   translate<AidlRect>(blocking));
+    return Error::NONE;
+}
 } // namespace Hwc2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 5c41982..6770017 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -139,7 +139,7 @@
     Error setLayerSurfaceDamage(Display display, Layer layer,
                                 const std::vector<IComposerClient::Rect>& damage) override;
     Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
-    Error setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) override;
+    Error setLayerColor(Display display, Layer layer, const Color& color) override;
     Error setLayerCompositionType(
             Display display, Layer layer,
             aidl::android::hardware::graphics::composer3::Composition type) override;
@@ -180,7 +180,8 @@
     Error setLayerPerFrameMetadataBlobs(
             Display display, Layer layer,
             const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
-    Error setDisplayBrightness(Display display, float brightness) override;
+    Error setDisplayBrightness(Display display, float brightness,
+                               const DisplayBrightnessOptions& options) override;
 
     // Composer HAL 2.4
     Error getDisplayCapabilities(
@@ -207,7 +208,14 @@
     Error getClientTargetProperty(Display display,
                                   IComposerClient::ClientTargetProperty* outClientTargetProperty,
                                   float* outClientTargetWhitePointNits) override;
+
+    // AIDL Composer HAL
     Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
+    Error setLayerBlockingRegion(Display display, Layer layer,
+                                 const std::vector<IComposerClient::Rect>& blocking) override;
+    Error setBootDisplayConfig(Display displayId, Config) override;
+    Error clearBootDisplayConfig(Display displayId) override;
+    Error getPreferredBootDisplayConfig(Display displayId, Config*) 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 f491a00..22f424f 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -31,6 +31,7 @@
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
 
+#include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
@@ -78,6 +79,9 @@
     enum class OptionalFeature {
         RefreshRateSwitching,
         ExpectedPresentTime,
+        // Whether setDisplayBrightness is able to be applied as part of a display command.
+        DisplayBrightnessCommand,
+        BootDisplayConfig,
     };
 
     virtual bool isSupported(OptionalFeature) const = 0;
@@ -162,8 +166,9 @@
                                         const std::vector<IComposerClient::Rect>& damage) = 0;
     virtual Error setLayerBlendMode(Display display, Layer layer,
                                     IComposerClient::BlendMode mode) = 0;
-    virtual Error setLayerColor(Display display, Layer layer,
-                                const IComposerClient::Color& color) = 0;
+    virtual Error setLayerColor(
+            Display display, Layer layer,
+            const aidl::android::hardware::graphics::composer3::Color& color) = 0;
     virtual Error setLayerCompositionType(
             Display display, Layer layer,
             aidl::android::hardware::graphics::composer3::Composition type) = 0;
@@ -204,7 +209,20 @@
                                             DisplayedFrameStats* outStats) = 0;
     virtual Error setLayerPerFrameMetadataBlobs(
             Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0;
-    virtual Error setDisplayBrightness(Display display, float brightness) = 0;
+    // Options for setting the display brightness
+    struct DisplayBrightnessOptions {
+        // If true, then immediately submits a brightness change request to composer. Otherwise,
+        // submission of the brightness change may be deferred until presenting the next frame.
+        // applyImmediately should only be false if OptionalFeature::DisplayBrightnessCommand is
+        // supported.
+        bool applyImmediately = true;
+
+        bool operator==(const DisplayBrightnessOptions& other) const {
+            return applyImmediately == other.applyImmediately;
+        }
+    };
+    virtual Error setDisplayBrightness(Display display, float brightness,
+                                       const DisplayBrightnessOptions& options) = 0;
 
     // Composer HAL 2.4
     virtual Error getDisplayCapabilities(
@@ -231,12 +249,18 @@
                                                 const std::vector<uint8_t>& value) = 0;
     virtual V2_4::Error getLayerGenericMetadataKeys(
             std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
+
     virtual Error getClientTargetProperty(
             Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
             float* outWhitePointNits) = 0;
 
     // AIDL Composer
     virtual Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) = 0;
+    virtual Error setLayerBlockingRegion(Display display, Layer layer,
+                                         const std::vector<IComposerClient::Rect>& blocking) = 0;
+    virtual Error setBootDisplayConfig(Display displayId, Config) = 0;
+    virtual Error clearBootDisplayConfig(Display displayId) = 0;
+    virtual Error getPreferredBootDisplayConfig(Display displayId, Config*) = 0;
 };
 
 } // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 3123351..d41a856 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -26,7 +26,7 @@
 #include <ui/DisplayId.h>
 #include <ui/Size.h>
 
-#include "DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 548d839..05e3aef 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -37,8 +37,7 @@
 #include <iterator>
 #include <set>
 
-#include "ComposerHal.h"
-
+using aidl::android::hardware::graphics::composer3::Color;
 using aidl::android::hardware::graphics::composer3::Composition;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 
@@ -532,13 +531,29 @@
     return error;
 }
 
-std::future<Error> Display::setDisplayBrightness(float brightness) {
-    return ftl::defer([composer = &mComposer, id = mId, brightness] {
-        const auto intError = composer->setDisplayBrightness(id, brightness);
+std::future<Error> Display::setDisplayBrightness(
+        float brightness, const Hwc2::Composer::DisplayBrightnessOptions& options) {
+    return ftl::defer([composer = &mComposer, id = mId, brightness, options] {
+        const auto intError = composer->setDisplayBrightness(id, brightness, options);
         return static_cast<Error>(intError);
     });
 }
 
+Error Display::setBootDisplayConfig(hal::HWConfigId configId) {
+    auto intError = mComposer.setBootDisplayConfig(mId, configId);
+    return static_cast<Error>(intError);
+}
+
+Error Display::clearBootDisplayConfig() {
+    auto intError = mComposer.clearBootDisplayConfig(mId);
+    return static_cast<Error>(intError);
+}
+
+Error Display::getPreferredBootDisplayConfig(hal::HWConfigId* configId) const {
+    auto intError = mComposer.getPreferredBootDisplayConfig(mId, configId);
+    return static_cast<Error>(intError);
+}
+
 Error Display::setAutoLowLatencyMode(bool on) {
     auto intError = mComposer.setAutoLowLatencyMode(mId, on);
     return static_cast<Error>(intError);
@@ -584,6 +599,21 @@
 
 // Layer methods
 
+namespace {
+std::vector<Hwc2::IComposerClient::Rect> convertRegionToHwcRects(const Region& region) {
+    size_t rectCount = 0;
+    Rect const* rectArray = region.getArray(&rectCount);
+
+    std::vector<Hwc2::IComposerClient::Rect> hwcRects;
+    hwcRects.reserve(rectCount);
+    for (size_t rect = 0; rect < rectCount; ++rect) {
+        hwcRects.push_back({rectArray[rect].left, rectArray[rect].top, rectArray[rect].right,
+                            rectArray[rect].bottom});
+    }
+    return hwcRects;
+}
+} // namespace
+
 Layer::~Layer() = default;
 
 namespace impl {
@@ -674,15 +704,7 @@
         intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId,
                                                    std::vector<Hwc2::IComposerClient::Rect>());
     } else {
-        size_t rectCount = 0;
-        auto rectArray = damage.getArray(&rectCount);
-
-        std::vector<Hwc2::IComposerClient::Rect> hwcRects;
-        for (size_t rect = 0; rect < rectCount; ++rect) {
-            hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
-                    rectArray[rect].right, rectArray[rect].bottom});
-        }
-
+        const auto hwcRects = convertRegionToHwcRects(damage);
         intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId, hwcRects);
     }
 
@@ -870,16 +892,7 @@
         return Error::NONE;
     }
     mVisibleRegion = region;
-
-    size_t rectCount = 0;
-    auto rectArray = region.getArray(&rectCount);
-
-    std::vector<Hwc2::IComposerClient::Rect> hwcRects;
-    for (size_t rect = 0; rect < rectCount; ++rect) {
-        hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
-                rectArray[rect].right, rectArray[rect].bottom});
-    }
-
+    const auto hwcRects = convertRegionToHwcRects(region);
     auto intError = mComposer.setLayerVisibleRegion(mDisplay->getId(), mId, hwcRects);
     return static_cast<Error>(intError);
 }
@@ -934,6 +947,21 @@
     return static_cast<Error>(intError);
 }
 
+Error Layer::setBlockingRegion(const Region& region) {
+    if (CC_UNLIKELY(!mDisplay)) {
+        return Error::BAD_DISPLAY;
+    }
+
+    if (region.isRect() && mBlockingRegion.isRect() &&
+        (region.getBounds() == mBlockingRegion.getBounds())) {
+        return Error::NONE;
+    }
+    mBlockingRegion = region;
+    const auto hwcRects = convertRegionToHwcRects(region);
+    const auto intError = mComposer.setLayerBlockingRegion(mDisplay->getId(), mId, hwcRects);
+    return static_cast<Error>(intError);
+}
+
 } // namespace impl
 } // namespace HWC2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 731d7f6..57eb128 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -34,8 +34,10 @@
 #include <unordered_set>
 #include <vector>
 
+#include "ComposerHal.h"
 #include "Hal.h"
 
+#include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
@@ -143,10 +145,15 @@
             nsecs_t expectedPresentTime, uint32_t* outNumTypes, uint32_t* outNumRequests,
             android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
     [[clang::warn_unused_result]] virtual std::future<hal::Error> setDisplayBrightness(
-            float brightness) = 0;
+            float brightness, const Hwc2::Composer::DisplayBrightnessOptions& options) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setActiveConfigWithConstraints(
             hal::HWConfigId configId, const hal::VsyncPeriodChangeConstraints& constraints,
             hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setBootDisplayConfig(
+            hal::HWConfigId configId) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error clearBootDisplayConfig() = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getPreferredBootDisplayConfig(
+            hal::HWConfigId* configId) const = 0;
     [[clang::warn_unused_result]] virtual hal::Error setAutoLowLatencyMode(bool on) = 0;
     [[clang::warn_unused_result]] virtual hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>*) const = 0;
@@ -211,10 +218,14 @@
                                  uint32_t* outNumRequests,
                                  android::sp<android::Fence>* outPresentFence,
                                  uint32_t* state) override;
-    std::future<hal::Error> setDisplayBrightness(float brightness) override;
+    std::future<hal::Error> setDisplayBrightness(
+            float brightness, const Hwc2::Composer::DisplayBrightnessOptions& options) override;
     hal::Error setActiveConfigWithConstraints(hal::HWConfigId configId,
                                               const hal::VsyncPeriodChangeConstraints& constraints,
                                               hal::VsyncPeriodChangeTimeline* outTimeline) override;
+    hal::Error setBootDisplayConfig(hal::HWConfigId configId) override;
+    hal::Error clearBootDisplayConfig() override;
+    hal::Error getPreferredBootDisplayConfig(hal::HWConfigId* configId) const override;
     hal::Error setAutoLowLatencyMode(bool on) override;
     hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>* outSupportedContentTypes) const override;
@@ -277,7 +288,8 @@
             const android::Region& damage) = 0;
 
     [[clang::warn_unused_result]] virtual hal::Error setBlendMode(hal::BlendMode mode) = 0;
-    [[clang::warn_unused_result]] virtual hal::Error setColor(hal::Color color) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setColor(
+            aidl::android::hardware::graphics::composer3::Color color) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setCompositionType(
             aidl::android::hardware::graphics::composer3::Composition type) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setDataspace(hal::Dataspace dataspace) = 0;
@@ -305,6 +317,8 @@
 
     // AIDL HAL
     [[clang::warn_unused_result]] virtual hal::Error setWhitePointNits(float whitePointNits) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setBlockingRegion(
+            const android::Region& region) = 0;
 };
 
 namespace impl {
@@ -328,7 +342,7 @@
     hal::Error setSurfaceDamage(const android::Region& damage) override;
 
     hal::Error setBlendMode(hal::BlendMode mode) override;
-    hal::Error setColor(hal::Color color) override;
+    hal::Error setColor(aidl::android::hardware::graphics::composer3::Color color) override;
     hal::Error setCompositionType(
             aidl::android::hardware::graphics::composer3::Composition type) override;
     hal::Error setDataspace(hal::Dataspace dataspace) override;
@@ -351,6 +365,7 @@
 
     // AIDL HAL
     hal::Error setWhitePointNits(float whitePointNits) override;
+    hal::Error setBlockingRegion(const android::Region& region) override;
 
 private:
     // These are references to data owned by HWC2::Device, which will outlive
@@ -366,6 +381,7 @@
     // multiple times.
     android::Region mVisibleRegion = android::Region::INVALID_REGION;
     android::Region mDamageRegion = android::Region::INVALID_REGION;
+    android::Region mBlockingRegion = android::Region::INVALID_REGION;
     hal::Dataspace mDataSpace = hal::Dataspace::UNKNOWN;
     android::HdrMetadata mHdrMetadata;
     android::mat4 mColorMatrix;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 546e677..44e4597 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -782,12 +782,13 @@
     return NO_ERROR;
 }
 
-std::future<status_t> HWComposer::setDisplayBrightness(PhysicalDisplayId displayId,
-                                                       float brightness) {
+std::future<status_t> HWComposer::setDisplayBrightness(
+        PhysicalDisplayId displayId, float brightness,
+        const Hwc2::Composer::DisplayBrightnessOptions& options) {
     RETURN_IF_INVALID_DISPLAY(displayId, ftl::yield<status_t>(BAD_INDEX));
     auto& display = mDisplayData[displayId].hwcDisplay;
 
-    return ftl::chain(display->setDisplayBrightness(brightness))
+    return ftl::chain(display->setDisplayBrightness(brightness, options))
             .then([displayId](hal::Error error) -> status_t {
                 if (error == hal::Error::UNSUPPORTED) {
                     RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
@@ -800,6 +801,52 @@
             });
 }
 
+bool HWComposer::getBootDisplayModeSupport() {
+    return mComposer->isSupported(Hwc2::Composer::OptionalFeature::BootDisplayConfig);
+}
+
+status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId,
+                                        hal::HWConfigId displayModeId) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setBootDisplayConfig(displayModeId);
+    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::clearBootDisplayMode(PhysicalDisplayId displayId) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->clearBootDisplayConfig();
+    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;
+}
+
+hal::HWConfigId HWComposer::getPreferredBootDisplayMode(PhysicalDisplayId displayId) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    hal::HWConfigId displayModeId = -1;
+    const auto error =
+            mDisplayData[displayId].hwcDisplay->getPreferredBootDisplayConfig(&displayModeId);
+    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 displayModeId;
+}
+
 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 69adfcd..93773fa 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -26,6 +26,7 @@
 #include <vector>
 
 #include <android-base/thread_annotations.h>
+#include <ui/DisplayIdentification.h>
 #include <ui/FenceTime.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -38,7 +39,6 @@
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 
-#include "DisplayIdentification.h"
 #include "DisplayMode.h"
 #include "HWC2.h"
 #include "Hal.h"
@@ -192,7 +192,9 @@
                                                DisplayedFrameStats* outStats) = 0;
 
     // Sets the brightness of a display.
-    virtual std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) = 0;
+    virtual std::future<status_t> setDisplayBrightness(
+            PhysicalDisplayId, float brightness,
+            const Hwc2::Composer::DisplayBrightnessOptions&) = 0;
 
     // Events handling ---------------------------------------------------------
 
@@ -253,6 +255,12 @@
 
     virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
     virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
+
+    // Composer 3.0
+    virtual bool getBootDisplayModeSupport() = 0;
+    virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0;
+    virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0;
+    virtual hal::HWConfigId getPreferredBootDisplayMode(PhysicalDisplayId) = 0;
 };
 
 namespace impl {
@@ -340,7 +348,9 @@
                                               uint64_t maxFrames) override;
     status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
                                        DisplayedFrameStats* outStats) override;
-    std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) override;
+    std::future<status_t> setDisplayBrightness(
+            PhysicalDisplayId, float brightness,
+            const Hwc2::Composer::DisplayBrightnessOptions&) override;
 
     // Events handling ---------------------------------------------------------
 
@@ -377,6 +387,12 @@
 
     const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
 
+    // Composer 3.0
+    bool getBootDisplayModeSupport() override;
+    status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) override;
+    status_t clearBootDisplayMode(PhysicalDisplayId) override;
+    hal::HWConfigId getPreferredBootDisplayMode(PhysicalDisplayId) 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 e33dc0f..40c9761 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -51,7 +51,6 @@
 
 using Attribute = IComposerClient::Attribute;
 using BlendMode = IComposerClient::BlendMode;
-using Color = IComposerClient::Color;
 using Connection = IComposerCallback::Connection;
 using ContentType = IComposerClient::ContentType;
 using Capability = IComposer::Capability;
@@ -136,6 +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";
         default:
             return "Unknown";
     }
diff --git a/services/surfaceflinger/DisplayHardware/Hash.cpp b/services/surfaceflinger/DisplayHardware/Hash.cpp
deleted file mode 100644
index 6056c8d..0000000
--- a/services/surfaceflinger/DisplayHardware/Hash.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "DisplayIdentification"
-
-#include <cstring>
-#include <type_traits>
-
-#include <log/log.h>
-
-#include "Hash.h"
-
-namespace android {
-namespace {
-
-template <class T>
-inline T load(const void* p) {
-    static_assert(std::is_integral<T>::value, "T must be integral");
-
-    T r;
-    std::memcpy(&r, p, sizeof(r));
-    return r;
-}
-
-uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
-    return (val >> shift) | (val << (64 - shift));
-}
-
-uint64_t shiftMix(uint64_t val) {
-    return val ^ (val >> 47);
-}
-
-uint64_t hash64Len16(uint64_t u, uint64_t v) {
-    constexpr uint64_t kMul = 0x9ddfea08eb382d69;
-    uint64_t a = (u ^ v) * kMul;
-    a ^= (a >> 47);
-    uint64_t b = (v ^ a) * kMul;
-    b ^= (b >> 47);
-    b *= kMul;
-    return b;
-}
-
-uint64_t hash64Len0To16(const char* s, uint64_t len) {
-    constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
-    constexpr uint64_t k3 = 0xc949d7c7509e6557;
-
-    if (len > 8) {
-        const uint64_t a = load<uint64_t>(s);
-        const uint64_t b = load<uint64_t>(s + len - 8);
-        return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
-    }
-    if (len >= 4) {
-        const uint32_t a = load<uint32_t>(s);
-        const uint32_t b = load<uint32_t>(s + len - 4);
-        return hash64Len16(len + (a << 3), b);
-    }
-    if (len > 0) {
-        const unsigned char a = static_cast<unsigned char>(s[0]);
-        const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
-        const unsigned char c = static_cast<unsigned char>(s[len - 1]);
-        const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
-        const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
-        return shiftMix(y * k2 ^ z * k3) * k2;
-    }
-    return k2;
-}
-
-} // namespace
-
-uint64_t cityHash64Len0To16(std::string_view sv) {
-    auto len = sv.length();
-    if (len > 16) {
-        ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
-        len = 16;
-    }
-    return hash64Len0To16(sv.data(), len);
-}
-
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/Hash.h b/services/surfaceflinger/DisplayHardware/Hash.h
deleted file mode 100644
index a7b6c71..0000000
--- a/services/surfaceflinger/DisplayHardware/Hash.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <string_view>
-
-namespace android {
-
-// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
-uint64_t cityHash64Len0To16(std::string_view sv);
-
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 7946002..d3acecb 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -161,6 +161,8 @@
         case OptionalFeature::RefreshRateSwitching:
             return mClient_2_4 != nullptr;
         case OptionalFeature::ExpectedPresentTime:
+        case OptionalFeature::DisplayBrightnessCommand:
+        case OptionalFeature::BootDisplayConfig:
             return false;
     }
 }
@@ -625,11 +627,29 @@
     return Error::NONE;
 }
 
-Error HidlComposer::setLayerColor(Display display, Layer layer,
-                                  const IComposerClient::Color& color) {
+static IComposerClient::Color to_hidl_type(
+        aidl::android::hardware::graphics::composer3::Color color) {
+    const auto floatColorToUint8Clamped = [](float val) -> uint8_t {
+        const auto intVal = static_cast<uint64_t>(std::round(255.0f * val));
+        const auto minVal = static_cast<uint64_t>(0);
+        const auto maxVal = static_cast<uint64_t>(255);
+        return std::clamp(intVal, minVal, maxVal);
+    };
+
+    return IComposerClient::Color{
+            floatColorToUint8Clamped(color.r),
+            floatColorToUint8Clamped(color.g),
+            floatColorToUint8Clamped(color.b),
+            floatColorToUint8Clamped(color.a),
+    };
+}
+
+Error HidlComposer::setLayerColor(
+        Display display, Layer layer,
+        const aidl::android::hardware::graphics::composer3::Color& color) {
     mWriter.selectDisplay(display);
     mWriter.selectLayer(layer);
-    mWriter.setLayerColor(color);
+    mWriter.setLayerColor(to_hidl_type(color));
     return Error::NONE;
 }
 
@@ -1013,7 +1033,8 @@
     return Error::NONE;
 }
 
-Error HidlComposer::setDisplayBrightness(Display display, float brightness) {
+Error HidlComposer::setDisplayBrightness(Display display, float brightness,
+                                         const DisplayBrightnessOptions&) {
     if (!mClient_2_3) {
         return Error::UNSUPPORTED;
     }
@@ -1196,6 +1217,18 @@
     return error;
 }
 
+Error HidlComposer::setBootDisplayConfig(Display /*displayId*/, Config) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::clearBootDisplayConfig(Display /*displayId*/) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::getPreferredBootDisplayConfig(Display /*displayId*/, Config*) {
+    return Error::UNSUPPORTED;
+}
+
 Error HidlComposer::getClientTargetProperty(
         Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
         float* outWhitePointNits) {
@@ -1208,6 +1241,11 @@
     return Error::NONE;
 }
 
+Error HidlComposer::setLayerBlockingRegion(Display, Layer,
+                                           const std::vector<IComposerClient::Rect>&) {
+    return Error::NONE;
+}
+
 CommandReader::~CommandReader() {
     resetData();
 }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 3b62fe0..c8c7800 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -248,7 +248,8 @@
     Error setLayerSurfaceDamage(Display display, Layer layer,
                                 const std::vector<IComposerClient::Rect>& damage) override;
     Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
-    Error setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) override;
+    Error setLayerColor(Display display, Layer layer,
+                        const aidl::android::hardware::graphics::composer3::Color& color) override;
     Error setLayerCompositionType(
             Display display, Layer layer,
             aidl::android::hardware::graphics::composer3::Composition type) override;
@@ -289,7 +290,8 @@
     Error setLayerPerFrameMetadataBlobs(
             Display display, Layer layer,
             const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
-    Error setDisplayBrightness(Display display, float brightness) override;
+    Error setDisplayBrightness(Display display, float brightness,
+                               const DisplayBrightnessOptions& options) override;
 
     // Composer HAL 2.4
     Error getDisplayCapabilities(
@@ -316,7 +318,14 @@
     Error getClientTargetProperty(Display display,
                                   IComposerClient::ClientTargetProperty* outClientTargetProperty,
                                   float* outWhitePointNits) override;
+
+    // AIDL Composer HAL
     Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
+    Error setLayerBlockingRegion(Display display, Layer layer,
+                                 const std::vector<IComposerClient::Rect>& blocking) override;
+    Error setBootDisplayConfig(Display displayId, Config) override;
+    Error clearBootDisplayConfig(Display displayId) override;
+    Error getPreferredBootDisplayConfig(Display displayId, Config*) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 5c2390e..6f02843 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -93,13 +93,6 @@
 
 void PowerAdvisor::onBootFinished() {
     mBootFinished.store(true);
-    {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* halWrapper = getPowerHal();
-        if (halWrapper != nullptr && usePowerHintSession()) {
-            mPowerHintSessionRunning = halWrapper->startPowerHintSession();
-        }
-    }
 }
 
 void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
@@ -156,7 +149,6 @@
 // checks both if it supports and if it's enabled
 bool PowerAdvisor::usePowerHintSession() {
     // uses cached value since the underlying support and flag are unlikely to change at runtime
-    ALOGE_IF(!mPowerHintEnabled.has_value(), "Power hint session cannot be used before boot!");
     return mPowerHintEnabled.value_or(false) && supportsPowerHintSession();
 }
 
@@ -175,10 +167,7 @@
 }
 
 void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
-    // we check "supports" here not "usePowerHintSession" because this needs to work
-    // before the session is actually running, and "use" will always fail before boot
-    // we store the values passed in before boot to start the session with during onBootFinished
-    if (!supportsPowerHintSession()) {
+    if (!usePowerHintSession()) {
         ALOGV("Power hint session target duration cannot be set, skipping");
         return;
     }
@@ -186,24 +175,7 @@
         std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper != nullptr) {
-            halWrapper->setTargetWorkDuration(targetDurationNanos);
-        }
-    }
-}
-
-void PowerAdvisor::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) {
-    // we check "supports" here not "usePowerHintSession" because this needs to wsork
-    // before the session is actually running, and "use" will always fail before boot.
-    // we store the values passed in before boot to start the session with during onBootFinished
-    if (!supportsPowerHintSession()) {
-        ALOGV("Power hint session thread ids cannot be set, skipping");
-        return;
-    }
-    {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* const halWrapper = getPowerHal();
-        if (halWrapper != nullptr) {
-            halWrapper->setPowerHintSessionThreadIds(const_cast<std::vector<int32_t>&>(threadIds));
+            halWrapper->setTargetWorkDuration(targetDurationNanos - kTargetSafetyMargin.count());
         }
     }
 }
@@ -227,6 +199,21 @@
     mPowerHintEnabled = enabled;
 }
 
+bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) {
+    if (!usePowerHintSession()) {
+        ALOGI("Power hint session cannot be started, skipping");
+    }
+    {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* halWrapper = getPowerHal();
+        if (halWrapper != nullptr && usePowerHintSession()) {
+            halWrapper->setPowerHintSessionThreadIds(threadIds);
+            mPowerHintSessionRunning = halWrapper->startPowerHintSession();
+        }
+    }
+    return mPowerHintSessionRunning;
+}
+
 class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
 public:
     HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
@@ -307,12 +294,7 @@
             mHasDisplayUpdateImminent = false;
         }
 
-        // This just gives a number not a binder status, so no .isOk()
-        mSupportsPowerHints = mPowerHal->getInterfaceVersion() >= 2;
-
-        if (mSupportsPowerHints) {
-            mPowerHintQueue.reserve(MAX_QUEUE_SIZE);
-        }
+        mSupportsPowerHint = checkPowerHintSessionSupported();
     }
 
     ~AidlPowerHalWrapper() override {
@@ -356,7 +338,14 @@
     }
 
     // only version 2+ of the aidl supports power hint sessions, hidl has no support
-    bool supportsPowerHintSession() override { return mSupportsPowerHints; }
+    bool supportsPowerHintSession() override { return mSupportsPowerHint; }
+
+    bool checkPowerHintSessionSupported() {
+        int64_t unused;
+        // Try to get preferred rate to determine if hint sessions are supported
+        // We check for isOk not EX_UNSUPPORTED_OPERATION to lump other errors
+        return mPowerHal->getHintSessionPreferredRate(&unused).isOk();
+    }
 
     bool isPowerHintSessionRunning() override { return mPowerHintSession != nullptr; }
 
@@ -382,38 +371,43 @@
     }
 
     bool startPowerHintSession() override {
-        if (mPowerHintSession != nullptr || !mPowerHintTargetDuration.has_value() ||
-            mPowerHintThreadIds.empty()) {
+        if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) {
             ALOGV("Cannot start power hint session, skipping");
             return false;
         }
         auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
-                                                mPowerHintThreadIds, *mPowerHintTargetDuration,
+                                                mPowerHintThreadIds, mTargetDuration,
                                                 &mPowerHintSession);
         if (!ret.isOk()) {
             ALOGW("Failed to start power hint session with error: %s",
                   ret.exceptionToString(ret.exceptionCode()).c_str());
-            // Indicate to the poweradvisor that this wrapper likely needs to be remade
-            mShouldReconnectHal = true;
+        } else {
+            mLastTargetDurationSent = mTargetDuration;
         }
         return isPowerHintSessionRunning();
     }
 
     bool shouldSetTargetDuration(int64_t targetDurationNanos) {
-        if (!mLastTargetDurationSent.has_value()) {
-            return true;
-        }
-
         // report if the change in target from our last submission to now exceeds the threshold
         return abs(1.0 -
-                   static_cast<double>(*mLastTargetDurationSent) /
+                   static_cast<double>(mLastTargetDurationSent) /
                            static_cast<double>(targetDurationNanos)) >=
-                ALLOWED_TARGET_DEVIATION_PERCENT;
+                kAllowedTargetDeviationPercent;
     }
 
     void setTargetWorkDuration(int64_t targetDurationNanos) override {
-        mPowerHintTargetDuration = targetDurationNanos;
-        if (shouldSetTargetDuration(targetDurationNanos) && isPowerHintSessionRunning()) {
+        ATRACE_CALL();
+        mTargetDuration = targetDurationNanos;
+        if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos);
+        if (!sNormalizeTarget && shouldSetTargetDuration(targetDurationNanos) &&
+            isPowerHintSessionRunning()) {
+            if (mLastActualDurationSent.has_value()) {
+                // update the error term here since we are actually sending an update to powerhal
+                if (sTraceHintSessionData)
+                    ATRACE_INT64("Target error term",
+                                 targetDurationNanos - *mLastActualDurationSent);
+            }
+            ALOGV("Sending target time: %lld ns", static_cast<long long>(targetDurationNanos));
             mLastTargetDurationSent = targetDurationNanos;
             auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
             if (!ret.isOk()) {
@@ -425,24 +419,29 @@
     }
 
     bool shouldReportActualDurationsNow() {
-        // report if we have never reported before or have exceeded the max queue size
-        if (!mLastMessageReported.has_value() || mPowerHintQueue.size() >= MAX_QUEUE_SIZE) {
+        // report if we have never reported before or are approaching a stale session
+        if (!mLastActualDurationSent.has_value() ||
+            (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
             return true;
         }
 
+        if (!mActualDuration.has_value()) {
+            return false;
+        }
+
         // duration of most recent timing
-        const double mostRecentActualDuration =
-                static_cast<double>(mPowerHintQueue.back().durationNanos);
+        const double mostRecentActualDuration = static_cast<double>(*mActualDuration);
         // duration of the last timing actually reported to the powerhal
-        const double lastReportedActualDuration =
-                static_cast<double>(mLastMessageReported->durationNanos);
+        const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent);
 
         // report if the change in duration from then to now exceeds the threshold
         return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
-                ALLOWED_ACTUAL_DEVIATION_PERCENT;
+                kAllowedActualDeviationPercent;
     }
 
     void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override {
+        ATRACE_CALL();
+
         if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
             ALOGV("Failed to send actual work duration, skipping");
             return;
@@ -450,13 +449,32 @@
 
         WorkDuration duration;
         duration.durationNanos = actualDurationNanos;
+        mActualDuration = actualDurationNanos;
+
+        // normalize the sent values to a pre-set target
+        if (sNormalizeTarget) {
+            duration.durationNanos += mLastTargetDurationSent - mTargetDuration;
+        }
         duration.timeStampNanos = timeStampNanos;
         mPowerHintQueue.push_back(duration);
 
+        long long targetNsec = mTargetDuration;
+        long long durationNsec = actualDurationNanos;
+
+        if (sTraceHintSessionData) {
+            ATRACE_INT64("Measured duration", durationNsec);
+            ATRACE_INT64("Target error term", targetNsec - durationNsec);
+        }
+
+        ALOGV("Sending actual work duration of: %lld on target: %lld with error: %lld",
+              durationNsec, targetNsec, targetNsec - durationNsec);
+
         // This rate limiter queues similar duration reports to the powerhal into
         // batches to avoid excessive binder calls. The criteria to send a given batch
         // are outlined in shouldReportActualDurationsNow()
         if (shouldReportActualDurationsNow()) {
+            ALOGV("Sending hint update batch");
+            mLastActualReportTimestamp = systemTime();
             auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
             if (!ret.isOk()) {
                 ALOGW("Failed to report actual work durations with error: %s",
@@ -464,7 +482,8 @@
                 mShouldReconnectHal = true;
             }
             mPowerHintQueue.clear();
-            mLastMessageReported = duration;
+            // we save the non-normalized value here to detect % changes
+            mLastActualDurationSent = actualDurationNanos;
         }
     }
 
@@ -472,32 +491,51 @@
 
     std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; }
 
-    std::optional<int64_t> getTargetWorkDuration() override { return mPowerHintTargetDuration; }
+    std::optional<int64_t> getTargetWorkDuration() override { return mTargetDuration; }
 
 private:
-    // max number of messages allowed in mPowerHintQueue before reporting is forced
-    static constexpr int32_t MAX_QUEUE_SIZE = 15;
-    // max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
-    static constexpr double ALLOWED_ACTUAL_DEVIATION_PERCENT = 0.1;
-    // max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
-    static constexpr double ALLOWED_TARGET_DEVIATION_PERCENT = 0.05;
-
     const sp<IPower> mPowerHal = nullptr;
     bool mHasExpensiveRendering = false;
     bool mHasDisplayUpdateImminent = false;
-    bool mShouldReconnectHal = false; // used to indicate an error state and need for reconstruction
+    // Used to indicate an error state and need for reconstruction
+    bool mShouldReconnectHal = false;
     // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
     sp<IPowerHintSession> mPowerHintSession = nullptr;
+    // Queue of actual durations saved to report
     std::vector<WorkDuration> mPowerHintQueue;
-    // halwrapper owns these values so we can init when we want and reconnect if broken
-    std::optional<int64_t> mPowerHintTargetDuration;
+    // The latest un-normalized values we have received for target and actual
+    int64_t mTargetDuration = kDefaultTarget.count();
+    std::optional<int64_t> mActualDuration;
+    // The list of thread ids, stored so we can restart the session from this class if needed
     std::vector<int32_t> mPowerHintThreadIds;
-    // keep track of the last messages sent for rate limiter change detection
-    std::optional<WorkDuration> mLastMessageReported;
-    std::optional<int64_t> mLastTargetDurationSent;
-    bool mSupportsPowerHints;
+    bool mSupportsPowerHint;
+    // Keep track of the last messages sent for rate limiter change detection
+    std::optional<int64_t> mLastActualDurationSent;
+    // timestamp of the last report we sent, used to avoid stale sessions
+    int64_t mLastActualReportTimestamp = 0;
+    int64_t mLastTargetDurationSent = kDefaultTarget.count();
+    // Whether to normalize all the actual values as error terms relative to a constant target
+    // This saves a binder call by not setting the target, and should not affect the pid values
+    static const bool sNormalizeTarget;
+    // Whether we should emit ATRACE_INT data for hint sessions
+    static const bool sTraceHintSessionData;
+    // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
+    static constexpr double kAllowedActualDeviationPercent = 0.1;
+    // Max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
+    static constexpr double kAllowedTargetDeviationPercent = 0.05;
+    // Target used for init and normalization, the actual value does not really matter
+    static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms;
+    // amount of time after the last message was sent before the session goes stale
+    // actually 100ms but we use 80 here to ideally avoid going stale
+    static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
 };
 
+const bool AidlPowerHalWrapper::sTraceHintSessionData =
+        base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
+
+const bool AidlPowerHalWrapper::sNormalizeTarget =
+        base::GetBoolProperty(std::string("debug.sf.normalize_hint_session_durations"), true);
+
 PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
     static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
     static bool sHasHal = true;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index b8fd17d..0db56aa 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -17,12 +17,15 @@
 #pragma once
 
 #include <atomic>
+#include <chrono>
 #include <unordered_set>
 
 #include <utils/Mutex.h>
 
+#include <ui/DisplayIdentification.h>
 #include "../Scheduler/OneShotTimer.h"
-#include "DisplayIdentification.h"
+
+using namespace std::chrono_literals;
 
 namespace android {
 
@@ -44,9 +47,9 @@
     virtual bool supportsPowerHintSession() = 0;
     virtual bool isPowerHintSessionRunning() = 0;
     virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
-    virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
     virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0;
     virtual void enablePowerHint(bool enabled) = 0;
+    virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
 };
 
 namespace impl {
@@ -86,9 +89,9 @@
     bool supportsPowerHintSession() override;
     bool isPowerHintSessionRunning() override;
     void setTargetWorkDuration(int64_t targetDurationNanos) override;
-    void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
     void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override;
     void enablePowerHint(bool enabled) override;
+    bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
 
 private:
     HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
@@ -100,6 +103,12 @@
     std::optional<bool> mSupportsPowerHint;
     bool mPowerHintSessionRunning = false;
 
+    // An adjustable safety margin which moves the "target" earlier to allow flinger to
+    // go a bit over without dropping a frame, especially since we can't measure
+    // the exact time HWC finishes composition so "actual" durations are measured
+    // from the end of present() instead, which is a bit later.
+    static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 2ms;
+
     std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
 
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 7720713..307da41 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -25,7 +25,7 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <ui/DisplayId.h>
 
-#include "DisplayIdentification.h"
+#include <ui/DisplayIdentification.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5948a78..7af1237 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,7 +101,9 @@
     if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
     if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
         layerFlags |= layer_state_t::eLayerSkipScreenshot;
-
+    if (args.sequence) {
+        sSequence = *args.sequence + 1;
+    }
     mDrawingState.flags = layerFlags;
     mDrawingState.active_legacy.transform.set(0, 0);
     mDrawingState.crop.makeInvalid();
@@ -943,16 +945,10 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
-bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
-        bool allowNonRectPreservingTransforms) {
+bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
     ui::Transform t;
     t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
 
-    if (!allowNonRectPreservingTransforms && !t.preserveRects()) {
-        ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER nor "
-              "ROTATE_SURFACE_FLINGER ignored");
-        return false;
-    }
     mDrawingState.sequence++;
     mDrawingState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
     mDrawingState.modified = true;
@@ -2022,9 +2018,9 @@
 void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
                                      const DisplayDevice* display) {
     const ui::Transform transform = getTransform();
-    auto buffer = getBuffer();
+    auto buffer = getExternalTexture();
     if (buffer != nullptr) {
-        LayerProtoHelper::writeToProto(buffer,
+        LayerProtoHelper::writeToProto(*buffer,
                                        [&]() { return layerInfo->mutable_active_buffer(); });
         LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
                                                  layerInfo->mutable_buffer_transform());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 31cdf0b..991256b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -98,6 +98,7 @@
     uid_t callingUid;
     uint32_t textureName;
     std::optional<uint32_t> sequence = std::nullopt;
+    bool addToRoot = true;
 };
 
 class Layer : public virtual RefBase, compositionengine::LayerFE {
@@ -366,8 +367,7 @@
     // Set a 2x2 transformation matrix on the layer. This transform
     // will be applied after parent transforms, but before any final
     // producer specified transform.
-    virtual bool setMatrix(const layer_state_t::matrix22_t& matrix,
-                           bool allowNonRectPreservingTransforms);
+    virtual bool setMatrix(const layer_state_t::matrix22_t& matrix);
 
     // This second set of geometry attributes are controlled by
     // setGeometryAppliesWithResize, and their default mode is to be
@@ -416,8 +416,10 @@
     // Used only to set BufferStateLayer state
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
-    virtual bool setBuffer(const BufferData&, nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
-                           bool /*isAutoTimestamp*/, std::optional<nsecs_t> /* dequeueTime */,
+    virtual bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
+                           const BufferData& /* bufferData */, nsecs_t /* postTime */,
+                           nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+                           std::optional<nsecs_t> /* dequeueTime */,
                            const FrameTimelineInfo& /*info*/) {
         return false;
     };
@@ -563,6 +565,9 @@
     virtual uint32_t getBufferTransform() const { return 0; }
 
     virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
+    virtual const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const {
+        return mDrawingState.buffer;
+    };
 
     virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
 
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index ee23561..015caa6 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -154,16 +154,16 @@
     }
 }
 
-void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer,
+void LayerProtoHelper::writeToProto(const renderengine::ExternalTexture& buffer,
                                     std::function<ActiveBufferProto*()> getActiveBufferProto) {
-    if (buffer->getWidth() != 0 || buffer->getHeight() != 0 || buffer->getStride() != 0 ||
-        buffer->format != 0) {
+    if (buffer.getBuffer()->getWidth() != 0 || buffer.getBuffer()->getHeight() != 0 ||
+        buffer.getBuffer()->getUsage() != 0 || buffer.getBuffer()->getPixelFormat() != 0) {
         // Use a lambda do avoid writing the object header when the object is empty
         ActiveBufferProto* activeBufferProto = getActiveBufferProto();
-        activeBufferProto->set_width(buffer->getWidth());
-        activeBufferProto->set_height(buffer->getHeight());
-        activeBufferProto->set_stride(buffer->getStride());
-        activeBufferProto->set_format(buffer->format);
+        activeBufferProto->set_width(buffer.getBuffer()->getWidth());
+        activeBufferProto->set_height(buffer.getBuffer()->getHeight());
+        activeBufferProto->set_format(buffer.getBuffer()->getPixelFormat());
+        activeBufferProto->set_usage(buffer.getBuffer()->getUsage());
     }
 }
 
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 249ec42..6ade143 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -15,6 +15,7 @@
  */
 
 #include <layerproto/LayerProtoHeader.h>
+#include <renderengine/ExternalTexture.h>
 
 #include <Layer.h>
 #include <gui/WindowInfo.h>
@@ -48,7 +49,7 @@
                                        TransformProto* transformProto);
     static void writeTransformToProto(const ui::Transform& transform,
                                       TransformProto* transformProto);
-    static void writeToProto(const sp<GraphicBuffer>& buffer,
+    static void writeToProto(const renderengine::ExternalTexture& buffer,
                              std::function<ActiveBufferProto*()> getActiveBufferProto);
     static void writeToProto(const gui::WindowInfo& inputInfo,
                              const wp<Layer>& touchableRegionBounds,
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index da8c3e0..ff30348 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -31,6 +31,7 @@
 #include <cutils/properties.h>
 #include <ftl/future.h>
 #include <gui/SyncScreenCaptureListener.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <ui/DisplayStatInfo.h>
 #include <utils/Trace.h>
 
@@ -351,8 +352,9 @@
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureSample: Buffer failed to allocate: %d",
                             bufferStatus);
         buffer = std::make_shared<
-                renderengine::ExternalTexture>(graphicBuffer, mFlinger.getRenderEngine(),
-                                               renderengine::ExternalTexture::Usage::WRITEABLE);
+                renderengine::impl::ExternalTexture>(graphicBuffer, mFlinger.getRenderEngine(),
+                                                     renderengine::impl::ExternalTexture::Usage::
+                                                             WRITEABLE);
     }
 
     auto captureScreenResultFuture =
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 409d098..5de796d 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -16,6 +16,8 @@
     ],
     shared_libs: [
         "libbase",
+        "libcutils",
+        "liblog",
         "libutils",
     ],
 }
@@ -26,10 +28,36 @@
     export_include_dirs: ["include"],
 }
 
+// TODO(b/185535769): Remove libsurfaceflinger_unittest's dependency on AsyncCallRecorder.
+cc_library_headers {
+    name: "libscheduler_test_headers",
+    defaults: ["libscheduler_defaults"],
+    export_include_dirs: ["tests"],
+}
+
 cc_library_static {
     name: "libscheduler",
     defaults: ["libscheduler_defaults"],
-    srcs: [],
+    srcs: [
+        "src/Timer.cpp",
+    ],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
+
+cc_test {
+    name: "libscheduler_test",
+    test_suites: ["device-tests"],
+    defaults: ["libscheduler_defaults"],
+    srcs: [
+        "tests/TimerTest.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+        "libscheduler",
+    ],
+    sanitize: {
+        address: true,
+    },
+}
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
new file mode 100644
index 0000000..d9d64ae
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrameRateOverrideMappings.h"
+
+namespace android::scheduler {
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+
+std::optional<Fps> FrameRateOverrideMappings::getFrameRateOverrideForUid(
+        uid_t uid, bool supportsFrameRateOverrideByContent) const {
+    std::lock_guard lock(mFrameRateOverridesLock);
+
+    {
+        const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
+        if (iter != mFrameRateOverridesFromBackdoor.end()) {
+            return iter->second;
+        }
+    }
+
+    {
+        const auto iter = mFrameRateOverridesFromGameManager.find(uid);
+        if (iter != mFrameRateOverridesFromGameManager.end()) {
+            return iter->second;
+        }
+    }
+
+    if (!supportsFrameRateOverrideByContent) {
+        return std::nullopt;
+    }
+
+    {
+        const auto iter = mFrameRateOverridesByContent.find(uid);
+        if (iter != mFrameRateOverridesByContent.end()) {
+            return iter->second;
+        }
+    }
+
+    return std::nullopt;
+}
+
+std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides() {
+    std::lock_guard lock(mFrameRateOverridesLock);
+    std::vector<FrameRateOverride> overrides;
+    overrides.reserve(std::max({mFrameRateOverridesFromGameManager.size(),
+                                mFrameRateOverridesFromBackdoor.size(),
+                                mFrameRateOverridesByContent.size()}));
+
+    for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+        overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+    }
+    for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
+        if (std::find_if(overrides.begin(), overrides.end(),
+                         [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
+            overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+        }
+    }
+    for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+        if (std::find_if(overrides.begin(), overrides.end(),
+                         [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
+            overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+        }
+    }
+
+    return overrides;
+}
+
+void FrameRateOverrideMappings::dump(std::string& result) const {
+    using base::StringAppendF;
+
+    std::lock_guard lock(mFrameRateOverridesLock);
+
+    StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
+    for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+        StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+    }
+    StringAppendF(&result, "}\n");
+
+    StringAppendF(&result, "Frame Rate Overrides (GameManager): {");
+    for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
+        StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+    }
+    StringAppendF(&result, "}\n");
+
+    StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
+    for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+        StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+    }
+    StringAppendF(&result, "}\n");
+}
+
+bool FrameRateOverrideMappings::updateFrameRateOverridesByContent(
+        const UidToFrameRateOverride& frameRateOverrides) {
+    std::lock_guard lock(mFrameRateOverridesLock);
+    if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
+                    frameRateOverrides.begin(), frameRateOverrides.end(),
+                    [](const auto& lhs, const auto& rhs) {
+                        return lhs.first == rhs.first && isApproxEqual(lhs.second, rhs.second);
+                    })) {
+        mFrameRateOverridesByContent = frameRateOverrides;
+        return true;
+    }
+    return false;
+}
+
+void FrameRateOverrideMappings::setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride) {
+    std::lock_guard lock(mFrameRateOverridesLock);
+    if (frameRateOverride.frameRateHz != 0.f) {
+        mFrameRateOverridesFromGameManager[frameRateOverride.uid] =
+                Fps::fromValue(frameRateOverride.frameRateHz);
+    } else {
+        mFrameRateOverridesFromGameManager.erase(frameRateOverride.uid);
+    }
+}
+
+void FrameRateOverrideMappings::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+    std::lock_guard lock(mFrameRateOverridesLock);
+    if (frameRateOverride.frameRateHz != 0.f) {
+        mFrameRateOverridesFromBackdoor[frameRateOverride.uid] =
+                Fps::fromValue(frameRateOverride.frameRateHz);
+    } else {
+        mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
+    }
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
new file mode 100644
index 0000000..278f87c
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <gui/DisplayEventReceiver.h>
+#include <scheduler/Fps.h>
+#include <sys/types.h>
+#include <map>
+#include <optional>
+
+namespace android::scheduler {
+class FrameRateOverrideMappings {
+    using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+    using UidToFrameRateOverride = std::map<uid_t, Fps>;
+
+public:
+    std::optional<Fps> getFrameRateOverrideForUid(uid_t uid,
+                                                  bool supportsFrameRateOverrideByContent) const
+            EXCLUDES(mFrameRateOverridesLock);
+    std::vector<FrameRateOverride> getAllFrameRateOverrides() EXCLUDES(mFrameRateOverridesLock);
+    void dump(std::string& result) const;
+    bool updateFrameRateOverridesByContent(const UidToFrameRateOverride& frameRateOverrides)
+            EXCLUDES(mFrameRateOverridesLock);
+    void setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride)
+            EXCLUDES(mFrameRateOverridesLock);
+    void setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride)
+            EXCLUDES(mFrameRateOverridesLock);
+
+private:
+    // The frame rate override lists need their own mutex as they are being read
+    // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
+    mutable std::mutex mFrameRateOverridesLock;
+
+    // mappings between a UID and a preferred refresh rate that this app would
+    // run at.
+    UidToFrameRateOverride mFrameRateOverridesByContent GUARDED_BY(mFrameRateOverridesLock);
+    UidToFrameRateOverride mFrameRateOverridesFromBackdoor GUARDED_BY(mFrameRateOverridesLock);
+    UidToFrameRateOverride mFrameRateOverridesFromGameManager GUARDED_BY(mFrameRateOverridesLock);
+};
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 74a2ca7..0efc28b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -33,7 +33,6 @@
 
 #include "../Layer.h"
 #include "LayerInfo.h"
-#include "SchedulerUtils.h"
 
 namespace android::scheduler {
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 2d88a4f..8a3b0b9 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -29,7 +29,6 @@
 
 #include "LayerHistory.h"
 #include "RefreshRateConfigs.h"
-#include "SchedulerUtils.h"
 
 namespace android {
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 71d5631..3becb5c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -206,7 +206,7 @@
 
     if (layer.vote == LayerVoteType::ExplicitExact) {
         const int divider = getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate);
-        if (mSupportsFrameRateOverride) {
+        if (mSupportsFrameRateOverrideByContent) {
             // Since we support frame rate override, allow refresh rates which are
             // multiples of the layer's request, as those apps would be throttled
             // down to run at the desired refresh rate.
@@ -225,7 +225,7 @@
     // The layer frame rate is not a divider of the refresh rate,
     // there is a small penalty attached to the score to favor the frame rates
     // the exactly matches the display refresh rate or a multiple.
-    constexpr float kNonExactMatchingPenalty = 0.99f;
+    constexpr float kNonExactMatchingPenalty = 0.95f;
     return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
             kNonExactMatchingPenalty;
 }
@@ -479,7 +479,7 @@
     const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
 
     const bool touchBoostForExplicitExact = [&] {
-        if (mSupportsFrameRateOverride) {
+        if (mSupportsFrameRateOverrideByContent) {
             // Enable touch boost if there are other layers besides exact
             return explicitExact + noVoteLayers != layers.size();
         } else {
@@ -547,7 +547,6 @@
         const std::vector<LayerRequirement>& layers, Fps displayFrameRate,
         GlobalSignals globalSignals) const {
     ATRACE_CALL();
-    if (!mSupportsFrameRateOverride) return {};
 
     ALOGV("getFrameRateOverrides %zu layers", layers.size());
     std::lock_guard lock(mLock);
@@ -618,7 +617,7 @@
         const auto [refreshRate, score] = *i;
         ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score);
 
-        ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100));
+        ATRACE_INT(refreshRate->getName().c_str(), static_cast<int>(std::round(score * 100)));
 
         if (score > max * (1 + kEpsilon)) {
             max = score;
@@ -714,20 +713,19 @@
 
 void RefreshRateConfigs::initializeIdleTimer() {
     if (mConfig.idleTimerTimeoutMs > 0) {
-        const auto getCallback = [this]() -> std::optional<IdleTimerCallbacks::Callbacks> {
-            std::scoped_lock lock(mIdleTimerCallbacksMutex);
-            if (!mIdleTimerCallbacks.has_value()) return {};
-            return mConfig.supportKernelIdleTimer ? mIdleTimerCallbacks->kernel
-                                                  : mIdleTimerCallbacks->platform;
-        };
-
         mIdleTimer.emplace(
                 "IdleTimer", std::chrono::milliseconds(mConfig.idleTimerTimeoutMs),
-                [getCallback] {
-                    if (const auto callback = getCallback()) callback->onReset();
+                [this] {
+                    std::scoped_lock lock(mIdleTimerCallbacksMutex);
+                    if (const auto callbacks = getIdleTimerCallbacks()) {
+                        callbacks->onReset();
+                    }
                 },
-                [getCallback] {
-                    if (const auto callback = getCallback()) callback->onExpired();
+                [this] {
+                    std::scoped_lock lock(mIdleTimerCallbacksMutex);
+                    if (const auto callbacks = getIdleTimerCallbacks()) {
+                        callbacks->onExpired();
+                    }
                 });
     }
 }
@@ -763,12 +761,12 @@
     mMinSupportedRefreshRate = sortedModes.front();
     mMaxSupportedRefreshRate = sortedModes.back();
 
-    mSupportsFrameRateOverride = false;
+    mSupportsFrameRateOverrideByContent = false;
     if (mConfig.enableFrameRateOverride) {
         for (const auto& mode1 : sortedModes) {
             for (const auto& mode2 : sortedModes) {
                 if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) {
-                    mSupportsFrameRateOverride = true;
+                    mSupportsFrameRateOverrideByContent = true;
                     break;
                 }
             }
@@ -1007,8 +1005,8 @@
         base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
     }
 
-    base::StringAppendF(&result, "Supports Frame Rate Override: %s\n",
-                        mSupportsFrameRateOverride ? "yes" : "no");
+    base::StringAppendF(&result, "Supports Frame Rate Override By Content: %s\n",
+                        mSupportsFrameRateOverrideByContent ? "yes" : "no");
     base::StringAppendF(&result, "Idle timer: (%s) %s\n",
                         mConfig.supportKernelIdleTimer ? "kernel" : "platform",
                         mIdleTimer ? mIdleTimer->dump().c_str() : "off");
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 4bbdab6..849d297 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -30,7 +30,6 @@
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/HWComposer.h"
 #include "Scheduler/OneShotTimer.h"
-#include "Scheduler/SchedulerUtils.h"
 #include "Scheduler/StrongTyping.h"
 
 namespace android::scheduler {
@@ -328,7 +327,7 @@
     // refresh rates.
     KernelIdleTimerAction getIdleTimerAction() const;
 
-    bool supportsFrameRateOverride() const { return mSupportsFrameRateOverride; }
+    bool supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; }
 
     // Return the display refresh rate divider to match the layer
     // frame rate, or 0 if the display refresh rate is not a multiple of the
@@ -348,16 +347,24 @@
 
     bool supportsKernelIdleTimer() const { return mConfig.supportKernelIdleTimer; }
 
-    void setIdleTimerCallbacks(std::function<void()> platformTimerReset,
-                               std::function<void()> platformTimerExpired,
-                               std::function<void()> kernelTimerReset,
-                               std::function<void()> kernelTimerExpired) {
+    struct IdleTimerCallbacks {
+        struct Callbacks {
+            std::function<void()> onReset;
+            std::function<void()> onExpired;
+        };
+
+        Callbacks platform;
+        Callbacks kernel;
+    };
+
+    void setIdleTimerCallbacks(IdleTimerCallbacks callbacks) EXCLUDES(mIdleTimerCallbacksMutex) {
         std::scoped_lock lock(mIdleTimerCallbacksMutex);
-        mIdleTimerCallbacks.emplace();
-        mIdleTimerCallbacks->platform.onReset = std::move(platformTimerReset);
-        mIdleTimerCallbacks->platform.onExpired = std::move(platformTimerExpired);
-        mIdleTimerCallbacks->kernel.onReset = std::move(kernelTimerReset);
-        mIdleTimerCallbacks->kernel.onExpired = std::move(kernelTimerExpired);
+        mIdleTimerCallbacks = std::move(callbacks);
+    }
+
+    void clearIdleTimerCallbacks() EXCLUDES(mIdleTimerCallbacksMutex) {
+        std::scoped_lock lock(mIdleTimerCallbacksMutex);
+        mIdleTimerCallbacks.reset();
     }
 
     void startIdleTimer() {
@@ -380,7 +387,7 @@
             return;
         }
         mIdleTimer->reset();
-    };
+    }
 
     void dump(std::string& result) const EXCLUDES(mLock);
 
@@ -448,6 +455,13 @@
 
     void initializeIdleTimer();
 
+    std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
+            REQUIRES(mIdleTimerCallbacksMutex) {
+        if (!mIdleTimerCallbacks) return {};
+        return mConfig.supportKernelIdleTimer ? mIdleTimerCallbacks->kernel
+                                              : mIdleTimerCallbacks->platform;
+    }
+
     // The list of refresh rates, indexed by display modes ID. This may change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
@@ -481,7 +495,7 @@
     const std::vector<Fps> mKnownFrameRates;
 
     const Config mConfig;
-    bool mSupportsFrameRateOverride;
+    bool mSupportsFrameRateOverrideByContent;
 
     struct GetBestRefreshRateInvocation {
         std::vector<LayerRequirement> layerRequirements;
@@ -492,21 +506,11 @@
     mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
             GUARDED_BY(mLock);
 
-    // Timer that records time between requests for next vsync.
-    std::optional<scheduler::OneShotTimer> mIdleTimer;
-
-    struct IdleTimerCallbacks {
-        struct Callbacks {
-            std::function<void()> onReset;
-            std::function<void()> onExpired;
-        };
-
-        Callbacks platform;
-        Callbacks kernel;
-    };
-
+    // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
     std::mutex mIdleTimerCallbacksMutex;
     std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
+    // Used to detect (lack of) frame activity.
+    std::optional<scheduler::OneShotTimer> mIdleTimer;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 23ebb06..f1ad755 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -25,7 +25,6 @@
 
 #include <scheduler/Fps.h>
 
-#include "Scheduler/SchedulerUtils.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android::scheduler {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index cbe4552..a85e748 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -42,9 +42,9 @@
 #include "../Layer.h"
 #include "DispSyncSource.h"
 #include "EventThread.h"
+#include "FrameRateOverrideMappings.h"
 #include "InjectVSyncSource.h"
 #include "OneShotTimer.h"
-#include "SchedulerUtils.h"
 #include "SurfaceFlingerProperties.h"
 #include "VSyncPredictor.h"
 #include "VSyncReactor.h"
@@ -62,6 +62,15 @@
 Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
       : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
 
+Scheduler::~Scheduler() {
+    // Stop timers and wait for their threads to exit.
+    mDisplayPowerTimer.reset();
+    mTouchTimer.reset();
+
+    // Stop idle timer and clear callbacks, as the RefreshRateConfigs may outlive the Scheduler.
+    setRefreshRateConfigs(nullptr);
+}
+
 void Scheduler::startTimers() {
     using namespace sysprop;
     using namespace std::string_literals;
@@ -84,11 +93,32 @@
     }
 }
 
-Scheduler::~Scheduler() {
-    // Ensure the OneShotTimer threads are joined before we start destroying state.
-    mDisplayPowerTimer.reset();
-    mTouchTimer.reset();
-    mRefreshRateConfigs.reset();
+void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
+    {
+        // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        if (mRefreshRateConfigs) {
+            mRefreshRateConfigs->stopIdleTimer();
+            mRefreshRateConfigs->clearIdleTimerCallbacks();
+        }
+    }
+    {
+        // Clear state that depends on the current instance.
+        std::scoped_lock lock(mPolicyLock);
+        mPolicy = {};
+    }
+
+    std::scoped_lock lock(mRefreshRateConfigsLock);
+    mRefreshRateConfigs = std::move(configs);
+    if (!mRefreshRateConfigs) return;
+
+    mRefreshRateConfigs->setIdleTimerCallbacks(
+            {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+                          .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+             .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+                        .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+    mRefreshRateConfigs->startIdleTimer();
 }
 
 void Scheduler::run() {
@@ -109,29 +139,11 @@
 }
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
-    {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        if (!mRefreshRateConfigs->supportsFrameRateOverride()) {
-            return std::nullopt;
-        }
-    }
-
-    std::lock_guard lock(mFrameRateOverridesLock);
-    {
-        const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
-        if (iter != mFrameRateOverridesFromBackdoor.end()) {
-            return iter->second;
-        }
-    }
-
-    {
-        const auto iter = mFrameRateOverridesByContent.find(uid);
-        if (iter != mFrameRateOverridesByContent.end()) {
-            return iter->second;
-        }
-    }
-
-    return std::nullopt;
+    const auto refreshRateConfigs = holdRefreshRateConfigs();
+    const bool supportsFrameRateOverrideByContent =
+            refreshRateConfigs->supportsFrameRateOverrideByContent();
+    return mFrameRateOverrideMappings
+            .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
 }
 
 bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
@@ -145,9 +157,6 @@
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
     std::scoped_lock lock(mRefreshRateConfigsLock);
-    if (!mRefreshRateConfigs->supportsFrameRateOverride()) {
-        return {};
-    }
 
     return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
         return !isVsyncValid(expectedVsyncTimestamp, uid);
@@ -253,18 +262,9 @@
 }
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
-    std::vector<FrameRateOverride> overrides;
-    {
-        std::lock_guard lock(mFrameRateOverridesLock);
-        for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
-            overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
-        }
-        for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
-            if (mFrameRateOverridesFromBackdoor.count(uid) == 0) {
-                overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
-            }
-        }
-    }
+    std::vector<FrameRateOverride> overrides =
+            mFrameRateOverrideMappings.getAllFrameRateOverrides();
+
     android::EventThread* thread;
     {
         std::lock_guard lock(mConnectionsLock);
@@ -664,20 +664,7 @@
                   mFeatures.test(Feature::kContentDetection) ? "on" : "off",
                   mLayerHistory.dump().c_str());
 
-    {
-        std::lock_guard lock(mFrameRateOverridesLock);
-        StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
-        for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
-            StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
-        }
-        StringAppendF(&result, "}\n");
-
-        StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
-        for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
-            StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
-        }
-        StringAppendF(&result, "}\n");
-    }
+    mFrameRateOverrideMappings.dump(result);
 
     {
         std::lock_guard lock(mHWVsyncLock);
@@ -694,7 +681,7 @@
 bool Scheduler::updateFrameRateOverrides(
         scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
     const auto refreshRateConfigs = holdRefreshRateConfigs();
-    if (!refreshRateConfigs->supportsFrameRateOverride()) {
+    if (!refreshRateConfigs->supportsFrameRateOverrideByContent()) {
         return false;
     }
 
@@ -702,15 +689,7 @@
         const auto frameRateOverrides =
                 refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
                                                           displayRefreshRate, consideredSignals);
-        std::lock_guard lock(mFrameRateOverridesLock);
-        if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
-                        frameRateOverrides.begin(), frameRateOverrides.end(),
-                        [](const auto& lhs, const auto& rhs) {
-                            return lhs.first == rhs.first && isApproxEqual(lhs.second, rhs.second);
-                        })) {
-            mFrameRateOverridesByContent = frameRateOverrides;
-            return true;
-        }
+        return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
     }
     return false;
 }
@@ -822,18 +801,20 @@
     mLayerHistory.setDisplayArea(displayArea);
 }
 
+void Scheduler::setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride) {
+    if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
+        return;
+    }
+
+    mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
+}
+
 void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
     if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
         return;
     }
 
-    std::lock_guard lock(mFrameRateOverridesLock);
-    if (frameRateOverride.frameRateHz != 0.f) {
-        mFrameRateOverridesFromBackdoor[frameRateOverride.uid] =
-                Fps::fromValue(frameRateOverride.frameRateHz);
-    } else {
-        mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
-    }
+    mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
 }
 
 std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 818f1ed..bc9024a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <atomic>
+#include <cstdint>
 #include <functional>
 #include <future>
 #include <memory>
@@ -34,13 +35,42 @@
 #include <scheduler/Features.h>
 
 #include "EventThread.h"
+#include "FrameRateOverrideMappings.h"
 #include "LayerHistory.h"
 #include "MessageQueue.h"
 #include "OneShotTimer.h"
 #include "RefreshRateConfigs.h"
-#include "SchedulerUtils.h"
 #include "VsyncSchedule.h"
 
+namespace android::scheduler {
+
+// Opaque handle to scheduler connection.
+struct ConnectionHandle {
+    using Id = std::uintptr_t;
+    static constexpr Id INVALID_ID = static_cast<Id>(-1);
+
+    Id id = INVALID_ID;
+
+    explicit operator bool() const { return id != INVALID_ID; }
+};
+
+inline bool operator==(ConnectionHandle lhs, ConnectionHandle rhs) {
+    return lhs.id == rhs.id;
+}
+
+} // namespace android::scheduler
+
+namespace std {
+
+template <>
+struct hash<android::scheduler::ConnectionHandle> {
+    size_t operator()(android::scheduler::ConnectionHandle handle) const {
+        return hash<android::scheduler::ConnectionHandle::Id>()(handle.id);
+    }
+};
+
+} // namespace std
+
 namespace android {
 
 class FenceTime;
@@ -74,11 +104,15 @@
 
 public:
     Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
-    ~Scheduler();
+    virtual ~Scheduler();
+
+    void startTimers();
+    void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs>)
+            EXCLUDES(mRefreshRateConfigsLock);
+
+    void run();
 
     void createVsyncSchedule(FeatureFlags);
-    void startTimers();
-    void run();
 
     using Impl::initVsync;
     using Impl::setInjector;
@@ -113,7 +147,7 @@
     void onScreenReleased(ConnectionHandle);
 
     void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
-            EXCLUDES(mFrameRateOverridesLock) EXCLUDES(mConnectionsLock);
+            EXCLUDES(mConnectionsLock);
 
     // Modifies work duration in the event thread.
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
@@ -163,8 +197,7 @@
 
     // Returns true if a given vsync timestamp is considered valid vsync
     // for a given uid
-    bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const
-            EXCLUDES(mFrameRateOverridesLock);
+    bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
 
     std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
 
@@ -193,40 +226,12 @@
 
     // Stores the preferred refresh rate that an app should run at.
     // FrameRateOverride.refreshRateHz == 0 means no preference.
-    void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesLock);
-    // Retrieves the overridden refresh rate for a given uid.
-    std::optional<Fps> getFrameRateOverride(uid_t uid) const
-            EXCLUDES(mRefreshRateConfigsLock, mFrameRateOverridesLock);
+    void setPreferredRefreshRateForUid(FrameRateOverride);
 
-    void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> refreshRateConfigs)
-            EXCLUDES(mRefreshRateConfigsLock) {
-        // We need to stop the idle timer on the previous RefreshRateConfigs instance
-        // and cleanup the scheduler's state before we switch to the other RefreshRateConfigs.
-        {
-            std::scoped_lock lock(mRefreshRateConfigsLock);
-            if (mRefreshRateConfigs) mRefreshRateConfigs->stopIdleTimer();
-        }
-        {
-            std::scoped_lock lock(mPolicyLock);
-            mPolicy = {};
-        }
-        {
-            std::scoped_lock lock(mRefreshRateConfigsLock);
-            mRefreshRateConfigs = std::move(refreshRateConfigs);
-            mRefreshRateConfigs->setIdleTimerCallbacks(
-                    [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Reset); },
-                    [this] {
-                        std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Expired);
-                    },
-                    [this] {
-                        std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Reset);
-                    },
-                    [this] {
-                        std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Expired);
-                    });
-            mRefreshRateConfigs->startIdleTimer();
-        }
-    }
+    void setGameModeRefreshRateForUid(FrameRateOverride);
+
+    // Retrieves the overridden refresh rate for a given uid.
+    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateConfigsLock);
 
     nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
         std::scoped_lock lock(mRefreshRateConfigsLock);
@@ -272,7 +277,7 @@
 
     void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
     bool updateFrameRateOverrides(RefreshRateConfigs::GlobalSignals, Fps displayRefreshRate)
-            REQUIRES(mPolicyLock) EXCLUDES(mFrameRateOverridesLock);
+            REQUIRES(mPolicyLock);
 
     impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
             EXCLUDES(mRefreshRateConfigsLock);
@@ -347,16 +352,7 @@
             GUARDED_BY(mVsyncTimelineLock);
     static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 
-    // The frame rate override lists need their own mutex as they are being read
-    // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
-    mutable std::mutex mFrameRateOverridesLock;
-
-    // mappings between a UID and a preferred refresh rate that this app would
-    // run at.
-    RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
-            GUARDED_BY(mFrameRateOverridesLock);
-    RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
-            GUARDED_BY(mFrameRateOverridesLock);
+    FrameRateOverrideMappings mFrameRateOverrideMappings;
 
     // Keeps track of whether the screen is acquired for debug
     std::atomic<bool> mScreenAcquired = false;
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
deleted file mode 100644
index e8e0444..0000000
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SchedulerUtils.h"
-
-#include <cinttypes>
-#include <numeric>
-#include <unordered_map>
-#include <vector>
-
-namespace android {
-namespace scheduler {
-
-int64_t calculate_median(std::vector<int64_t>* v) {
-    if (!v || v->empty()) {
-        return 0;
-    }
-
-    size_t n = v->size() / 2;
-    nth_element(v->begin(), v->begin() + static_cast<long>(n), v->end());
-    return v->at(n);
-}
-
-} // namespace scheduler
-} // namespace android
-
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
deleted file mode 100644
index 04a4cd1..0000000
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/Timers.h>
-#include <cinttypes>
-#include <numeric>
-#include <unordered_map>
-#include <vector>
-
-namespace android::scheduler {
-
-// Opaque handle to scheduler connection.
-struct ConnectionHandle {
-    using Id = std::uintptr_t;
-    static constexpr Id INVALID_ID = static_cast<Id>(-1);
-
-    Id id = INVALID_ID;
-
-    explicit operator bool() const { return id != INVALID_ID; }
-};
-
-inline bool operator==(ConnectionHandle lhs, ConnectionHandle rhs) {
-    return lhs.id == rhs.id;
-}
-
-// Calculates the statistical mean (average) in the data structure (array, vector). The
-// function does not modify the contents of the array.
-template <typename T>
-auto calculate_mean(const T& v) {
-    using V = typename T::value_type;
-    V sum = std::accumulate(v.begin(), v.end(), static_cast<V>(0));
-    return sum / static_cast<V>(v.size());
-}
-
-// Calculates the statistical median in the vector. Return 0 if the vector is empty. The
-// function modifies the vector contents.
-int64_t calculate_median(std::vector<int64_t>* v);
-
-// Calculates the statistical mode in the vector. Return 0 if the vector is empty.
-template <typename T>
-auto calculate_mode(const T& v) {
-    if (v.empty()) {
-        return 0;
-    }
-
-    // Create a map with all the counts for the indivicual values in the vector.
-    std::unordered_map<int64_t, int> counts;
-    for (int64_t value : v) {
-        counts[value]++;
-    }
-
-    // Sort the map, and return the number with the highest count. If two numbers have
-    // the same count, first one is returned.
-    using ValueType = const decltype(counts)::value_type&;
-    const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
-    return static_cast<int>(std::max_element(counts.begin(), counts.end(), compareCounts)->first);
-}
-
-template <class T, size_t N>
-constexpr size_t arrayLen(T (&)[N]) {
-    return N;
-}
-
-static constexpr size_t max64print = std::numeric_limits<nsecs_t>::digits10 + 1;
-
-template <typename T>
-static inline T round(float f) {
-    return static_cast<T>(std::round(f));
-}
-
-} // namespace android::scheduler
-
-namespace std {
-
-template <>
-struct hash<android::scheduler::ConnectionHandle> {
-    size_t operator()(android::scheduler::ConnectionHandle handle) const {
-        return hash<android::scheduler::ConnectionHandle::Id>()(handle.id);
-    }
-};
-
-} // namespace std
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index b52706f..2bfe204 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,17 +16,15 @@
 
 #pragma once
 
-#include <utils/Log.h>
-#include <utils/Timers.h>
 #include <functional>
 #include <optional>
 #include <string>
 
+#include <utils/Timers.h>
+
 #include "StrongTyping.h"
 
 namespace android::scheduler {
-class TimeKeeper;
-class VSyncTracker;
 
 using ScheduleResult = std::optional<nsecs_t>;
 
@@ -64,8 +62,7 @@
      *                          invocation of callbackFn.
      *
      */
-    virtual CallbackToken registerCallback(Callback const& callbackFn,
-                                           std::string callbackName) = 0;
+    virtual CallbackToken registerCallback(Callback, std::string callbackName) = 0;
 
     /*
      * Unregisters a callback.
@@ -142,8 +139,9 @@
 
 protected:
     VSyncDispatch() = default;
-    VSyncDispatch(VSyncDispatch const&) = delete;
-    VSyncDispatch& operator=(VSyncDispatch const&) = delete;
+
+    VSyncDispatch(const VSyncDispatch&) = delete;
+    VSyncDispatch& operator=(const VSyncDispatch&) = delete;
 };
 
 /*
@@ -152,11 +150,11 @@
  */
 class VSyncCallbackRegistration {
 public:
-    VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback const& callbackFn,
-                              std::string const& callbackName);
+    VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback, std::string callbackName);
+    ~VSyncCallbackRegistration();
+
     VSyncCallbackRegistration(VSyncCallbackRegistration&&);
     VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
-    ~VSyncCallbackRegistration();
 
     // See documentation for VSyncDispatch::schedule.
     ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
@@ -165,9 +163,6 @@
     CancelResult cancel();
 
 private:
-    VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete;
-    VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete;
-
     std::reference_wrapper<VSyncDispatch> mDispatch;
     VSyncDispatch::CallbackToken mToken;
     bool mValidToken;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index b805bf6..27f4311 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -15,18 +15,24 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <android-base/stringprintf.h>
-#include <utils/Trace.h>
+
 #include <vector>
 
-#include "TimeKeeper.h"
+#include <android-base/stringprintf.h>
+#include <ftl/concat.h>
+#include <utils/Trace.h>
+
+#include <scheduler/TimeKeeper.h>
+
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncTracker.h"
 
 namespace android::scheduler {
+
 using base::StringAppendF;
 
 namespace {
+
 nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
                                 const VSyncDispatch::ScheduleTiming& timing) {
     return nextVsyncTime - timing.readyDuration - timing.workDuration;
@@ -38,17 +44,17 @@
             std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
     return getExpectedCallbackTime(nextVsyncTime, timing);
 }
+
 } // namespace
 
 VSyncDispatch::~VSyncDispatch() = default;
 VSyncTracker::~VSyncTracker() = default;
-TimeKeeper::~TimeKeeper() = default;
 
-VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
-                                                           VSyncDispatch::Callback const& cb,
+VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string name,
+                                                           VSyncDispatch::Callback callback,
                                                            nsecs_t minVsyncDistance)
-      : mName(name),
-        mCallback(cb),
+      : mName(std::move(name)),
+        mCallback(std::move(callback)),
         mMinVsyncDistance(minVsyncDistance) {}
 
 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
@@ -222,16 +228,6 @@
     rearmTimerSkippingUpdateFor(now, mCallbacks.end());
 }
 
-void VSyncDispatchTimerQueue::TraceBuffer::note(std::string_view name, nsecs_t alarmIn,
-                                                nsecs_t vsFor) {
-    if (ATRACE_ENABLED()) {
-        snprintf(str_buffer.data(), str_buffer.size(), "%.4s%s%" PRId64 "%s%" PRId64,
-                 name.substr(0, kMaxNamePrint).data(), kTraceNamePrefix, alarmIn,
-                 kTraceNameSeparator, vsFor);
-    }
-    ATRACE_NAME(str_buffer.data());
-}
-
 void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
         nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
     std::optional<nsecs_t> min;
@@ -247,16 +243,18 @@
             callback->update(mTracker, now);
         }
         auto const wakeupTime = *callback->wakeupTime();
-        if (!min || (min && *min > wakeupTime)) {
+        if (!min || *min > wakeupTime) {
             nextWakeupName = callback->name();
             min = wakeupTime;
             targetVsync = callback->targetVsync();
         }
     }
 
-    if (min && (min < mIntendedWakeupTime)) {
-        if (targetVsync && nextWakeupName) {
-            mTraceBuffer.note(*nextWakeupName, *min - now, *targetVsync - now);
+    if (min && min < mIntendedWakeupTime) {
+        if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
+            ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
+                              "us; VSYNC in ", ns2us(*targetVsync - now), "us");
+            ATRACE_NAME(trace.c_str());
         }
         setTimer(*min, now);
     } else {
@@ -305,13 +303,13 @@
 }
 
 VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
-        Callback const& callbackFn, std::string callbackName) {
+        Callback callback, std::string callbackName) {
     std::lock_guard lock(mMutex);
     return CallbackToken{
             mCallbacks
                     .emplace(++mCallbackToken,
-                             std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
-                                                                            callbackFn,
+                             std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
+                                                                            std::move(callback),
                                                                             mMinVsyncDistance))
                     .first->first};
 }
@@ -406,10 +404,10 @@
 }
 
 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
-                                                     VSyncDispatch::Callback const& callbackFn,
-                                                     std::string const& callbackName)
+                                                     VSyncDispatch::Callback callback,
+                                                     std::string callbackName)
       : mDispatch(dispatch),
-        mToken(dispatch.registerCallback(callbackFn, callbackName)),
+        mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))),
         mValidToken(true) {}
 
 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 26237b6..3186d6d 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <android-base/thread_annotations.h>
-#include <array>
 #include <functional>
 #include <memory>
 #include <mutex>
@@ -25,11 +23,14 @@
 #include <string_view>
 #include <unordered_map>
 
-#include "SchedulerUtils.h"
+#include <android-base/thread_annotations.h>
+
 #include "VSyncDispatch.h"
 
 namespace android::scheduler {
 
+class VSyncTracker;
+
 // VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
 // VSyncDispatchTimerQueue hoisted to public for unit testing.
 class VSyncDispatchTimerQueueEntry {
@@ -38,7 +39,7 @@
     // Valid transition: disarmed -> armed ( when scheduled )
     // Valid transition: armed -> running -> disarmed ( when timer is called)
     // Valid transition: armed -> disarmed ( when cancelled )
-    VSyncDispatchTimerQueueEntry(std::string const& name, VSyncDispatch::Callback const& fn,
+    VSyncDispatchTimerQueueEntry(std::string name, VSyncDispatch::Callback,
                                  nsecs_t minVsyncDistance);
     std::string_view name() const;
 
@@ -47,10 +48,9 @@
     std::optional<nsecs_t> lastExecutedVsyncTarget() const;
 
     // This moves the state from disarmed->armed and will calculate the wakeupTime.
-    ScheduleResult schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker,
-                            nsecs_t now);
+    ScheduleResult schedule(VSyncDispatch::ScheduleTiming, VSyncTracker&, nsecs_t now);
     // This will update armed entries with the latest vsync information. Entry remains armed.
-    void update(VSyncTracker& tracker, nsecs_t now);
+    void update(VSyncTracker&, nsecs_t now);
 
     // This will return empty if not armed, or the next calculated wakeup time if armed.
     // It will not update the wakeupTime.
@@ -83,11 +83,11 @@
     void dump(std::string& result) const;
 
 private:
-    std::string const mName;
-    VSyncDispatch::Callback const mCallback;
+    const std::string mName;
+    const VSyncDispatch::Callback mCallback;
 
     VSyncDispatch::ScheduleTiming mScheduleTiming;
-    nsecs_t const mMinVsyncDistance;
+    const nsecs_t mMinVsyncDistance;
 
     struct ArmingInfo {
         nsecs_t mActualWakeupTime;
@@ -117,19 +117,19 @@
     //                                  should be grouped into one wakeup.
     // \param[in] minVsyncDistance      The minimum distance between two vsync estimates before the
     //                                  vsyncs are considered the same vsync event.
-    explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
-                                     nsecs_t timerSlack, nsecs_t minVsyncDistance);
+    VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper>, VSyncTracker&, nsecs_t timerSlack,
+                            nsecs_t minVsyncDistance);
     ~VSyncDispatchTimerQueue();
 
-    CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
-    void unregisterCallback(CallbackToken token) final;
-    ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) final;
-    CancelResult cancel(CallbackToken token) final;
-    void dump(std::string& result) const final;
+    CallbackToken registerCallback(Callback, std::string callbackName) final;
+    void unregisterCallback(CallbackToken) final;
+    ScheduleResult schedule(CallbackToken, ScheduleTiming) final;
+    CancelResult cancel(CallbackToken) final;
+    void dump(std::string&) const final;
 
 private:
-    VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
-    VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
+    VSyncDispatchTimerQueue(const VSyncDispatchTimerQueue&) = delete;
+    VSyncDispatchTimerQueue& operator=(const VSyncDispatchTimerQueue&) = delete;
 
     using CallbackMap =
             std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
@@ -153,17 +153,6 @@
     CallbackMap mCallbacks GUARDED_BY(mMutex);
     nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
 
-    struct TraceBuffer {
-        static constexpr char const kTraceNamePrefix[] = "-alarm in:";
-        static constexpr char const kTraceNameSeparator[] = " for vs:";
-        static constexpr size_t kMaxNamePrint = 4;
-        static constexpr size_t kNumTsPrinted = 2;
-        static constexpr size_t maxlen = kMaxNamePrint + arrayLen(kTraceNamePrefix) +
-                arrayLen(kTraceNameSeparator) - 1 + (kNumTsPrinted * max64print);
-        std::array<char, maxlen> str_buffer;
-        void note(std::string_view name, nsecs_t in, nsecs_t vs);
-    } mTraceBuffer GUARDED_BY(mMutex);
-
     // For debugging purposes
     nsecs_t mLastTimerCallback GUARDED_BY(mMutex) = kInvalidTime;
     nsecs_t mLastTimerSchedule GUARDED_BY(mMutex) = kInvalidTime;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index e9bd92a..61d2fb7 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -18,24 +18,27 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wextra"
 
+#undef LOG_TAG
+#define LOG_TAG "VSyncPredictor"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-#include "VSyncPredictor.h"
+
+#include <algorithm>
+#include <chrono>
+#include <sstream>
+
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
-#include <algorithm>
-#include <chrono>
-#include <sstream>
-#include "RefreshRateConfigs.h"
 
-#undef LOG_TAG
-#define LOG_TAG "VSyncPredictor"
+#include "RefreshRateConfigs.h"
+#include "VSyncPredictor.h"
 
 namespace android::scheduler {
+
 using base::StringAppendF;
 
 static auto constexpr kMaxPercent = 100u;
@@ -121,7 +124,8 @@
         mTimestamps[mLastTimestampIndex] = timestamp;
     }
 
-    if (mTimestamps.size() < kMinimumSamplesForPrediction) {
+    const size_t numSamples = mTimestamps.size();
+    if (numSamples < kMinimumSamplesForPrediction) {
         mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
         return true;
     }
@@ -141,36 +145,44 @@
     //
     // intercept = mean(Y) - slope * mean(X)
     //
-    std::vector<nsecs_t> vsyncTS(mTimestamps.size());
-    std::vector<nsecs_t> ordinals(mTimestamps.size());
+    std::vector<nsecs_t> vsyncTS(numSamples);
+    std::vector<nsecs_t> ordinals(numSamples);
 
-    // normalizing to the oldest timestamp cuts down on error in calculating the intercept.
-    auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
+    // Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
+    const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
     auto it = mRateMap.find(mIdealPeriod);
     auto const currentPeriod = it->second.slope;
-    // TODO (b/144707443): its important that there's some precision in the mean of the ordinals
-    //                     for the intercept calculation, so scale the ordinals by 1000 to continue
-    //                     fixed point calculation. Explore expanding
-    //                     scheduler::utils::calculate_mean to have a fixed point fractional part.
-    static constexpr int64_t kScalingFactor = 1000;
 
-    for (auto i = 0u; i < mTimestamps.size(); i++) {
+    // The mean of the ordinals must be precise for the intercept calculation, so scale them up for
+    // fixed-point arithmetic.
+    constexpr int64_t kScalingFactor = 1000;
+
+    nsecs_t meanTS = 0;
+    nsecs_t meanOrdinal = 0;
+
+    for (size_t i = 0; i < numSamples; i++) {
         traceInt64If("VSP-ts", mTimestamps[i]);
 
-        vsyncTS[i] = mTimestamps[i] - oldest_ts;
-        ordinals[i] = ((vsyncTS[i] + (currentPeriod / 2)) / currentPeriod) * kScalingFactor;
+        const auto timestamp = mTimestamps[i] - oldestTS;
+        vsyncTS[i] = timestamp;
+        meanTS += timestamp;
+
+        const auto ordinal = (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor;
+        ordinals[i] = ordinal;
+        meanOrdinal += ordinal;
     }
 
-    auto meanTS = scheduler::calculate_mean(vsyncTS);
-    auto meanOrdinal = scheduler::calculate_mean(ordinals);
-    for (size_t i = 0; i < vsyncTS.size(); i++) {
+    meanTS /= numSamples;
+    meanOrdinal /= numSamples;
+
+    for (size_t i = 0; i < numSamples; i++) {
         vsyncTS[i] -= meanTS;
         ordinals[i] -= meanOrdinal;
     }
 
-    auto top = 0ll;
-    auto bottom = 0ll;
-    for (size_t i = 0; i < vsyncTS.size(); i++) {
+    nsecs_t top = 0;
+    nsecs_t bottom = 0;
+    for (size_t i = 0; i < numSamples; i++) {
         top += vsyncTS[i] * ordinals[i];
         bottom += ordinals[i] * ordinals[i];
     }
@@ -365,4 +377,4 @@
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 40e6944..cfaf7d6 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -16,11 +16,12 @@
 
 #pragma once
 
-#include <android-base/thread_annotations.h>
 #include <mutex>
 #include <unordered_map>
 #include <vector>
-#include "SchedulerUtils.h"
+
+#include <android-base/thread_annotations.h>
+
 #include "VSyncTracker.h"
 
 namespace android::scheduler {
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 1c9de1c..bdcab51 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -18,21 +18,22 @@
 #undef LOG_TAG
 #define LOG_TAG "VSyncReactor"
 //#define LOG_NDEBUG 0
-#include "VSyncReactor.h"
+
 #include <cutils/properties.h>
 #include <log/log.h>
 #include <utils/Trace.h>
+
 #include "../TracedOrdinal.h"
-#include "TimeKeeper.h"
 #include "VSyncDispatch.h"
+#include "VSyncReactor.h"
 #include "VSyncTracker.h"
 
 namespace android::scheduler {
+
 using base::StringAppendF;
 
 VsyncController::~VsyncController() = default;
 
-Clock::~Clock() = default;
 nsecs_t SystemClock::now() const {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index a9d536b..6a1950a 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -16,14 +16,18 @@
 
 #pragma once
 
-#include <android-base/thread_annotations.h>
-#include <ui/FenceTime.h>
 #include <memory>
 #include <mutex>
 #include <unordered_map>
 #include <vector>
-#include "TimeKeeper.h"
+
+#include <android-base/thread_annotations.h>
+#include <ui/FenceTime.h>
+
+#include <scheduler/TimeKeeper.h>
+
 #include "VsyncController.h"
+
 namespace android::scheduler {
 
 class Clock;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 77d1223..e611658 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -15,10 +15,10 @@
  */
 
 #include <scheduler/Fps.h>
+#include <scheduler/Timer.h>
 
 #include "VsyncSchedule.h"
 
-#include "Timer.h"
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncPredictor.h"
 #include "VSyncReactor.h"
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/include/scheduler/TimeKeeper.h
similarity index 81%
rename from services/surfaceflinger/Scheduler/TimeKeeper.h
rename to services/surfaceflinger/Scheduler/include/scheduler/TimeKeeper.h
index 40dd841..319390b 100644
--- a/services/surfaceflinger/Scheduler/TimeKeeper.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/TimeKeeper.h
@@ -16,14 +16,17 @@
 
 #pragma once
 
-#include <utils/Timers.h>
 #include <functional>
+#include <string>
+
+#include <utils/Timers.h>
 
 namespace android::scheduler {
 
 class Clock {
 public:
     virtual ~Clock();
+
     /*
      * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
      */
@@ -31,8 +34,9 @@
 
 protected:
     Clock() = default;
-    Clock(Clock const&) = delete;
-    Clock& operator=(Clock const&) = delete;
+
+    Clock(const Clock&) = delete;
+    Clock& operator=(const Clock&) = delete;
 };
 
 /*
@@ -46,19 +50,20 @@
      * Arms callback to fired when time is current based on CLOCK_MONOTONIC
      * There is only one timer, and subsequent calls will reset the callback function and the time.
      */
-    virtual void alarmAt(std::function<void()> const& callback, nsecs_t time) = 0;
+    virtual void alarmAt(std::function<void()>, nsecs_t time) = 0;
 
     /*
      * Cancels an existing pending callback
      */
     virtual void alarmCancel() = 0;
 
-    virtual void dump(std::string& result) const = 0;
+    virtual void dump(std::string&) const = 0;
 
 protected:
-    TimeKeeper(TimeKeeper const&) = delete;
-    TimeKeeper& operator=(TimeKeeper const&) = delete;
     TimeKeeper() = default;
+
+    TimeKeeper(const TimeKeeper&) = delete;
+    TimeKeeper& operator=(const TimeKeeper&) = delete;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/include/scheduler/Timer.h
similarity index 80%
rename from services/surfaceflinger/Scheduler/Timer.h
rename to services/surfaceflinger/Scheduler/include/scheduler/Timer.h
index 628d800..58ad6cb 100644
--- a/services/surfaceflinger/Scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Timer.h
@@ -16,11 +16,14 @@
 
 #pragma once
 
-#include "TimeKeeper.h"
+#include <array>
+#include <functional>
+#include <mutex>
+#include <thread>
 
 #include <android-base/thread_annotations.h>
-#include <array>
-#include <thread>
+
+#include <scheduler/TimeKeeper.h>
 
 namespace android::scheduler {
 
@@ -28,13 +31,19 @@
 public:
     Timer();
     ~Timer();
+
     nsecs_t now() const final;
 
     // NB: alarmAt and alarmCancel are threadsafe; with the last-returning function being effectual
     //     Most users will want to serialize thes calls so as to be aware of the timer state.
-    void alarmAt(std::function<void()> const& cb, nsecs_t time) final;
+    void alarmAt(std::function<void()>, nsecs_t time) final;
     void alarmCancel() final;
-    void dump(std::string& result) const final;
+
+    void dump(std::string&) const final;
+
+protected:
+    // For unit testing
+    int mEpollFd = -1;
 
 private:
     enum class DebugState {
@@ -48,12 +57,12 @@
         ftl_last = Terminated
     };
 
-    void reset();
-    void cleanup();
-    void setDebugState(DebugState state) EXCLUDES(mMutex);
+    void reset() EXCLUDES(mMutex);
+    void cleanup() REQUIRES(mMutex);
+    void setDebugState(DebugState) EXCLUDES(mMutex);
 
     int mTimerFd = -1;
-    int mEpollFd = -1;
+
     std::array<int, 2> mPipes = {-1, -1};
 
     std::thread mDispatchThread;
@@ -63,6 +72,7 @@
 
     mutable std::mutex mMutex;
     std::function<void()> mCallback GUARDED_BY(mMutex);
+    bool mExpectingCallback GUARDED_BY(mMutex) = false;
     DebugState mDebugState GUARDED_BY(mMutex);
 };
 
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp
similarity index 81%
rename from services/surfaceflinger/Scheduler/Timer.cpp
rename to services/surfaceflinger/Scheduler/src/Timer.cpp
index 22c3a70..a4cf57f 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -16,7 +16,6 @@
 
 #undef LOG_TAG
 #define LOG_TAG "SchedulerTimer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <chrono>
 #include <cstdint>
@@ -25,19 +24,20 @@
 #include <sys/timerfd.h>
 #include <sys/unistd.h>
 
-#include <android-base/stringprintf.h>
+#include <ftl/concat.h>
 #include <ftl/enum.h>
 #include <log/log.h>
 #include <utils/Trace.h>
 
-#include "SchedulerUtils.h"
-#include "Timer.h"
+#include <scheduler/Timer.h>
 
 namespace android::scheduler {
-using base::StringAppendF;
 
-static constexpr size_t kReadPipe = 0;
-static constexpr size_t kWritePipe = 1;
+constexpr size_t kReadPipe = 0;
+constexpr size_t kWritePipe = 1;
+
+Clock::~Clock() = default;
+TimeKeeper::~TimeKeeper() = default;
 
 Timer::Timer() {
     reset();
@@ -51,13 +51,25 @@
 }
 
 void Timer::reset() {
-    cleanup();
-    mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
-    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
-    if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
-        ALOGE("could not create TimerDispatch mPipes");
-        return;
-    };
+    std::function<void()> cb;
+    {
+        std::lock_guard lock(mMutex);
+        if (mExpectingCallback && mCallback) {
+            cb = mCallback;
+        }
+
+        cleanup();
+        mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+        mEpollFd = epoll_create1(EPOLL_CLOEXEC);
+        if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
+            ALOGE("could not create TimerDispatch mPipes");
+        }
+    }
+    if (cb) {
+        setDebugState(DebugState::InCallback);
+        cb();
+        setDebugState(DebugState::Running);
+    }
     setDebugState(DebugState::Reset);
 }
 
@@ -81,6 +93,8 @@
         close(mPipes[kWritePipe]);
         mPipes[kWritePipe] = -1;
     }
+    mExpectingCallback = false;
+    mCallback = {};
 }
 
 void Timer::endDispatch() {
@@ -92,13 +106,14 @@
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
-void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) {
+void Timer::alarmAt(std::function<void()> callback, nsecs_t time) {
     std::lock_guard lock(mMutex);
     using namespace std::literals;
     static constexpr int ns_per_s =
             std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
 
-    mCallback = cb;
+    mCallback = std::move(callback);
+    mExpectingCallback = true;
 
     struct itimerspec old_timer;
     struct itimerspec new_timer {
@@ -165,9 +180,6 @@
     }
 
     uint64_t iteration = 0;
-    char const traceNamePrefix[] = "TimerIteration #";
-    static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print;
-    std::array<char, maxlen> str_buffer;
 
     while (true) {
         setDebugState(DebugState::Waiting);
@@ -176,9 +188,8 @@
 
         setDebugState(DebugState::Running);
         if (ATRACE_ENABLED()) {
-            snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix,
-                     iteration++);
-            ATRACE_NAME(str_buffer.data());
+            ftl::Concat trace("TimerIteration #", iteration++);
+            ATRACE_NAME(trace.c_str());
         }
 
         if (nfds == -1) {
@@ -198,6 +209,7 @@
                 {
                     std::lock_guard lock(mMutex);
                     cb = mCallback;
+                    mExpectingCallback = false;
                 }
                 if (cb) {
                     setDebugState(DebugState::InCallback);
@@ -221,7 +233,9 @@
 
 void Timer::dump(std::string& result) const {
     std::lock_guard lock(mMutex);
-    StringAppendF(&result, "\t\tDebugState: %s\n", ftl::enum_string(mDebugState).c_str());
+    result.append("\t\tDebugState: ");
+    result.append(ftl::enum_string(mDebugState));
+    result.push_back('\n');
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h b/services/surfaceflinger/Scheduler/tests/AsyncCallRecorder.h
similarity index 98%
rename from services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
rename to services/surfaceflinger/Scheduler/tests/AsyncCallRecorder.h
index 8bed766..57f0dab 100644
--- a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
+++ b/services/surfaceflinger/Scheduler/tests/AsyncCallRecorder.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/services/surfaceflinger/tests/unittests/TimerTest.cpp b/services/surfaceflinger/Scheduler/tests/TimerTest.cpp
similarity index 61%
rename from services/surfaceflinger/tests/unittests/TimerTest.cpp
rename to services/surfaceflinger/Scheduler/tests/TimerTest.cpp
index cda6bbf..47d968c 100644
--- a/services/surfaceflinger/tests/unittests/TimerTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/TimerTest.cpp
@@ -14,32 +14,48 @@
  * limitations under the License.
  */
 
-#include "AsyncCallRecorder.h"
-#include "Scheduler/TimeKeeper.h"
-#include "Scheduler/Timer.h"
-
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-using namespace testing;
-using namespace std::literals;
+#include <scheduler/TimeKeeper.h>
+#include <scheduler/Timer.h>
+
+#include "AsyncCallRecorder.h"
 
 namespace android::scheduler {
 
+struct TestableTimer : public Timer {
+public:
+    void makeEpollError() {
+        // close the epoll file descriptor to cause an epoll error
+        close(mEpollFd);
+    }
+};
+
 struct TimerTest : testing::Test {
-    static constexpr int mIterations = 20;
+    static constexpr int kIterations = 20;
 
     AsyncCallRecorder<void (*)()> mCallbackRecorder;
-    Timer mTimer;
+    TestableTimer mTimer;
 
     void timerCallback() { mCallbackRecorder.recordCall(); }
 };
 
 TEST_F(TimerTest, callsCallbackIfScheduledInPast) {
-    for (int i = 0; i < mIterations; i++) {
-        mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 10'000'00);
+    for (int i = 0; i < kIterations; i++) {
+        mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 1'000'000);
         EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value());
         EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
     }
 }
+
+TEST_F(TimerTest, recoversAfterEpollError) {
+    for (int i = 0; i < kIterations; i++) {
+        mTimer.makeEpollError();
+        mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 1'000'000);
+        EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value());
+        EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
+    }
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a6eeda2..74f733e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -65,6 +65,7 @@
 #include <private/gui/SyncFeatures.h>
 #include <processgroup/processgroup.h>
 #include <renderengine/RenderEngine.h>
+#include <renderengine/impl/ExternalTexture.h>
 #include <sys/types.h>
 #include <ui/ColorSpace.h>
 #include <ui/DataspaceUtils.h>
@@ -94,6 +95,7 @@
 #include <type_traits>
 #include <unordered_map>
 
+#include <ui/DisplayIdentification.h>
 #include "BackgroundExecutor.h"
 #include "BufferLayer.h"
 #include "BufferQueueLayer.h"
@@ -103,7 +105,6 @@
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
-#include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/Hal.h"
@@ -270,12 +271,6 @@
 }
 
 
-enum Permission {
-    ACCESS_SURFACE_FLINGER = 0x1,
-    ROTATE_SURFACE_FLINGER = 0x2,
-    INTERNAL_SYSTEM_WINDOW = 0x4,
-};
-
 struct IdleTimerConfig {
     int32_t timeoutMs;
     bool supportKernelIdleTimer;
@@ -339,7 +334,6 @@
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
-bool SurfaceFlinger::enableSdrDimming;
 LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
 
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
@@ -385,7 +379,7 @@
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
         mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
         mPowerAdvisor(*this),
-        mWindowInfosListenerInvoker(new WindowInfosListenerInvoker(this)) {
+        mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make(*this)) {
     ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
 }
 
@@ -503,15 +497,10 @@
 
     mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
 
-    // Debug property overrides ro. property
-    enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
-
     enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
 
-    mTransactionTracingEnabled =
-            !mIsUserBuild && property_get_bool("debug.sf.enable_transaction_tracing", true);
-    if (mTransactionTracingEnabled) {
-        mTransactionTracing.enable();
+    if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
+        mTransactionTracing.emplace();
     }
 }
 
@@ -721,7 +710,6 @@
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
 
     mFlagManager = std::make_unique<android::FlagManager>();
-    mPowerAdvisor.enablePowerHint(mFlagManager->use_adpf_cpu_hint());
     mFrameTracer->initialize();
     mFrameTimeline->onBootFinished();
 
@@ -751,7 +739,18 @@
         }
 
         readPersistentProperties();
+        std::optional<pid_t> renderEngineTid = getRenderEngine().getRenderEngineTid();
+        std::vector<int32_t> tidList;
+        tidList.emplace_back(gettid());
+        if (renderEngineTid.has_value()) {
+            tidList.emplace_back(*renderEngineTid);
+        }
         mPowerAdvisor.onBootFinished();
+        mPowerAdvisor.enablePowerHint(mFlagManager->use_adpf_cpu_hint());
+        if (mPowerAdvisor.usePowerHintSession()) {
+            mPowerAdvisor.startPowerHintSession(tidList);
+        }
+
         mBootStage = BootStage::FINISHED;
 
         if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
@@ -1015,6 +1014,9 @@
     info->secure = display->isSecure();
     info->deviceProductInfo = display->getDeviceProductInfo();
 
+    // TODO: Scale this to multiple displays.
+    info->installOrientation = display->isPrimary() ? internalDisplayOrientation : ui::ROTATION_0;
+
     return NO_ERROR;
 }
 
@@ -1420,6 +1422,52 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getBootDisplayModeSupport(bool* outSupport) const {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD mutable -> status_t {
+        *outSupport = getHwComposer().getBootDisplayModeSupport();
+        return NO_ERROR;
+    });
+    return future.get();
+}
+
+status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            return getHwComposer().setBootDisplayMode(*displayId, id);
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            return BAD_VALUE;
+        }
+    });
+    return future.get();
+}
+
+status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            return getHwComposer().clearBootDisplayMode(*displayId);
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            return BAD_VALUE;
+        }
+    });
+    return future.get();
+}
+
+status_t SurfaceFlinger::getPreferredBootDisplayMode(const sp<IBinder>& displayToken,
+                                                     ui::DisplayModeId* id) {
+    auto future = mScheduler->schedule([=]() MAIN_THREAD mutable -> status_t {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            *id = getHwComposer().getPreferredBootDisplayMode(*displayId);
+            return NO_ERROR;
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            return BAD_VALUE;
+        }
+    });
+    return future.get();
+}
+
 void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
     const char* const whence = __func__;
     static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
@@ -1599,7 +1647,7 @@
 status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea,
                                                    const sp<IBinder>& stopLayerHandle,
                                                    const sp<IRegionSamplingListener>& listener) {
-    if (!listener || samplingArea == Rect::INVALID_RECT) {
+    if (!listener || samplingArea == Rect::INVALID_RECT || samplingArea.isEmpty()) {
         return BAD_VALUE;
     }
 
@@ -1669,6 +1717,17 @@
     return NO_ERROR;
 }
 
+bool SurfaceFlinger::hasVisibleHdrLayer(const sp<DisplayDevice>& display) {
+    bool hasHdrLayers = false;
+    mDrawingState.traverse([&,
+                            compositionDisplay = display->getCompositionDisplay()](Layer* layer) {
+        hasHdrLayers |= (layer->isVisible() &&
+                         compositionDisplay->includesLayer(layer->getCompositionEngineLayerFE()) &&
+                         isHdrDataspace(layer->getDataSpace()));
+    });
+    return hasHdrLayers;
+}
+
 status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
                                               const gui::DisplayBrightness& brightness) {
     if (!displayToken) {
@@ -1678,13 +1737,30 @@
     const char* const whence = __func__;
     return ftl::chain(mScheduler->schedule([=]() MAIN_THREAD {
                if (const auto display = getDisplayDeviceLocked(displayToken)) {
-                   if (enableSdrDimming) {
+                   const bool supportsDisplayBrightnessCommand =
+                           getHwComposer().getComposer()->isSupported(
+                                   Hwc2::Composer::OptionalFeature::DisplayBrightnessCommand);
+                   // If we support applying display brightness as a command, then we also support
+                   // dimming SDR layers.
+                   if (supportsDisplayBrightnessCommand) {
                        display->getCompositionDisplay()
                                ->setDisplayBrightness(brightness.sdrWhitePointNits,
                                                       brightness.displayBrightnessNits);
+                       MAIN_THREAD_GUARD(display->stageBrightness(brightness.displayBrightness));
+                       if (hasVisibleHdrLayer(display)) {
+                           scheduleComposite(FrameHint::kNone);
+                       } else {
+                           scheduleCommit(FrameHint::kNone);
+                       }
+                       return ftl::yield<status_t>(OK);
+                   } else {
+                       return getHwComposer()
+                               .setDisplayBrightness(display->getPhysicalId(),
+                                                     brightness.displayBrightness,
+                                                     Hwc2::Composer::DisplayBrightnessOptions{
+                                                             .applyImmediately = true});
                    }
-                   return getHwComposer().setDisplayBrightness(display->getPhysicalId(),
-                                                               brightness.displayBrightness);
+
                } else {
                    ALOGE("%s: Invalid display token %p", whence, displayToken.get());
                    return ftl::yield<status_t>(NAME_NOT_FOUND);
@@ -1748,6 +1824,23 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+                                                     bool* outSupport) const {
+    if (!displayToken || !outSupport) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        return NAME_NOT_FOUND;
+    }
+    *outSupport =
+            getHwComposer().hasDisplayCapability(*displayId, DisplayCapability::DISPLAY_DECORATION);
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1922,6 +2015,13 @@
 }
 
 bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) {
+    MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
+    // we set this once at the beginning of commit to ensure consistency throughout the whole frame
+    mPowerHintSessionData.sessionEnabled = mPowerAdvisor.usePowerHintSession();
+    if (mPowerHintSessionData.sessionEnabled) {
+        mPowerHintSessionData.commitStart = systemTime();
+    }
+
     // calculate the expected present time once and use the cached
     // value throughout this frame to make sure all layers are
     // seeing this same value.
@@ -1935,6 +2035,10 @@
     const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
     mScheduledPresentTime = expectedVsyncTime;
 
+    if (mPowerHintSessionData.sessionEnabled) {
+        mPowerAdvisor.setTargetWorkDuration(mExpectedPresentTime -
+                                            mPowerHintSessionData.commitStart);
+    }
     const auto vsyncIn = [&] {
         if (!ATRACE_ENABLED()) return 0.f;
         return (mExpectedPresentTime - systemTime()) / 1e6f;
@@ -1997,7 +2101,10 @@
         // We received the present fence from the HWC, so we assume it successfully updated
         // the mode, hence we update SF.
         mSetActiveModePending = false;
-        ON_MAIN_THREAD(updateInternalStateWithChangedMode());
+        {
+            Mutex::Autolock lock(mStateLock);
+            updateInternalStateWithChangedMode();
+        }
     }
 
     if (framePending) {
@@ -2026,7 +2133,8 @@
 
         bool needsTraversal = false;
         if (clearTransactionFlags(eTransactionFlushNeeded)) {
-            needsTraversal = flushTransactionQueues(vsyncId);
+            needsTraversal |= commitCreatedLayers();
+            needsTraversal |= flushTransactionQueues(vsyncId);
         }
 
         const bool shouldCommit =
@@ -2062,18 +2170,28 @@
     {
         Mutex::Autolock _l(mStateLock);
         mScheduler->chooseRefreshRateForContent();
+        setActiveModeInHwcIfNeeded();
     }
 
-    ON_MAIN_THREAD(setActiveModeInHwcIfNeeded());
-
     updateCursorAsync();
     updateInputFlinger();
 
+    if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
+        // This will block and tracing should only be enabled for debugging.
+        mLayerTracing.notify(mVisibleRegionsDirty, frameTime);
+    }
+
+    MAIN_THREAD_GUARD(persistDisplayBrightness(mustComposite));
+
     return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
 }
 
 void SurfaceFlinger::composite(nsecs_t frameTime) {
     ATRACE_CALL();
+    MainThreadScopedGuard mainThreadGuard(SF_MAIN_THREAD);
+    if (mPowerHintSessionData.sessionEnabled) {
+        mPowerHintSessionData.compositeStart = systemTime();
+    }
 
     compositionengine::CompositionRefreshArgs refreshArgs;
     const auto& displays = ON_MAIN_THREAD(mDisplays);
@@ -2127,6 +2245,11 @@
     const auto presentTime = systemTime();
 
     mCompositionEngine->present(refreshArgs);
+
+    if (mPowerHintSessionData.sessionEnabled) {
+        mPowerHintSessionData.presentEnd = systemTime();
+    }
+
     mTimeStats->recordFrameDuration(frameTime, systemTime());
 
     mScheduler->onPostComposition(presentTime);
@@ -2159,13 +2282,9 @@
     modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
 
     mLayersWithQueuedFrames.clear();
-    if (mLayerTracingEnabled) {
+    if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and should only be used for debugging.
-        if (mVisibleRegionsDirty) {
-            mLayerTracing.notify("visibleRegionsDirty");
-        } else if (mLayerTracing.flagIsSet(LayerTracing::TRACE_BUFFERS)) {
-            mLayerTracing.notify("bufferLatched");
-        }
+        mLayerTracing.notify(mVisibleRegionsDirty, frameTime);
     }
 
     mVisibleRegionsWereDirtyThisFrame = mVisibleRegionsDirty; // Cache value for use in post-comp
@@ -2174,6 +2293,13 @@
     if (mCompositionEngine->needsAnotherUpdate()) {
         scheduleCommit(FrameHint::kNone);
     }
+
+    // calculate total render time for performance hinting if adpf cpu hint is enabled,
+    if (mPowerHintSessionData.sessionEnabled) {
+        const nsecs_t flingerDuration =
+                (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart);
+        mPowerAdvisor.sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd);
+    }
 }
 
 void SurfaceFlinger::updateLayerGeometry() {
@@ -3101,6 +3227,34 @@
     mInputWindowCommands.clear();
 }
 
+void SurfaceFlinger::persistDisplayBrightness(bool needsComposite) {
+    const bool supportsDisplayBrightnessCommand = getHwComposer().getComposer()->isSupported(
+            Hwc2::Composer::OptionalFeature::DisplayBrightnessCommand);
+    if (!supportsDisplayBrightnessCommand) {
+        return;
+    }
+
+    const auto& displays = ON_MAIN_THREAD(mDisplays);
+
+    for (const auto& [_, display] : displays) {
+        if (const auto brightness = display->getStagedBrightness(); brightness) {
+            if (!needsComposite) {
+                const status_t error =
+                        getHwComposer()
+                                .setDisplayBrightness(display->getPhysicalId(), *brightness,
+                                                      Hwc2::Composer::DisplayBrightnessOptions{
+                                                              .applyImmediately = true})
+                                .get();
+
+                ALOGE_IF(error != NO_ERROR,
+                         "Error setting display brightness for display %s: %d (%s)",
+                         display->getDebugName().c_str(), error, strerror(error));
+            }
+            display->persistBrightness(needsComposite);
+        }
+    }
+}
+
 void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
                                       std::vector<DisplayInfo>& outDisplayInfos) {
     struct Details {
@@ -3447,7 +3601,7 @@
 }
 
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
-                                        const sp<Layer>& lbc, const wp<Layer>& parent,
+                                        const sp<Layer>& layer, const wp<Layer>& parent,
                                         bool addToRoot, uint32_t* outTransformHint) {
     if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
         ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
@@ -3455,31 +3609,22 @@
         return NO_MEMORY;
     }
 
-    setLayerCreatedState(handle, lbc, parent, addToRoot);
+    {
+        std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+        mCreatedLayers.emplace_back(layer, parent, addToRoot);
+    }
 
-    // Create a transaction includes the initial parent and producer.
-    Vector<ComposerState> states;
-    Vector<DisplayState> displays;
-
-    ComposerState composerState;
-    composerState.state.what = layer_state_t::eLayerCreated;
-    composerState.state.surface = handle;
-    states.add(composerState);
-
-    lbc->updateTransformHint(mActiveDisplayTransformHint);
+    layer->updateTransformHint(mActiveDisplayTransformHint);
     if (outTransformHint) {
         *outTransformHint = mActiveDisplayTransformHint;
     }
     // attach this layer to the client
     if (client != nullptr) {
-        client->attachLayer(handle, lbc);
+        client->attachLayer(handle, layer);
     }
 
-    int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
-    return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr,
-                               InputWindowCommands{}, -1 /* desiredPresentTime */,
-                               true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {},
-                               transactionId);
+    setTransactionFlags(eTransactionNeeded);
+    return NO_ERROR;
 }
 
 uint32_t SurfaceFlinger::getTransactionFlags() const {
@@ -3613,8 +3758,8 @@
         }
     }
 
-    if (mTransactionTracingEnabled) {
-        mTransactionTracing.addCommittedTransactions(transactions, vsyncId);
+    if (mTransactionTracing) {
+        mTransactionTracing->addCommittedTransactions(transactions, vsyncId);
     }
     return needsTraversal;
 }
@@ -3665,9 +3810,9 @@
     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) {
+            state.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+            state.bufferData->acquireFence &&
+            state.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled) {
             ATRACE_NAME("transactionCanLatchUnsignaled");
             return true;
         }
@@ -3732,10 +3877,10 @@
 
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
-        const bool acquireFenceChanged =
-                s.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged);
-        if (acquireFenceChanged && s.bufferData.acquireFence && !allowLatchUnsignaled &&
-            s.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+        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;
         }
@@ -3837,19 +3982,20 @@
     ATRACE_CALL();
 
     uint32_t permissions =
-            callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0;
+        callingThreadHasUnscopedSurfaceFlingerAccess() ?
+        layer_state_t::Permission::ACCESS_SURFACE_FLINGER : 0;
     // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
     // permissions.
-    if ((permissions & Permission::ACCESS_SURFACE_FLINGER) ||
+    if ((permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
         callingThreadHasRotateSurfaceFlingerAccess()) {
-        permissions |= Permission::ROTATE_SURFACE_FLINGER;
+        permissions |= layer_state_t::Permission::ROTATE_SURFACE_FLINGER;
     }
 
     if (callingThreadHasInternalSystemWindowAccess()) {
-        permissions |= Permission::INTERNAL_SYSTEM_WINDOW;
+        permissions |= layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW;
     }
 
-    if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
+    if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) &&
         (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
         ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
         flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
@@ -3874,8 +4020,8 @@
         mBufferCountTracker.increment(state.surface->localBinder());
     });
 
-    if (mTransactionTracingEnabled) {
-        mTransactionTracing.addQueuedTransaction(state);
+    if (mTransactionTracing) {
+        mTransactionTracing->addQueuedTransaction(state);
     }
     queueTransaction(state);
 
@@ -3911,7 +4057,8 @@
 
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime,
+        ComposerState stateCopy = state;
+        clientStateFlags |= setClientStateLocked(frameTimelineInfo, stateCopy, desiredPresentTime,
                                                  isAutoTimestamp, postTime, permissions);
         if ((flags & eAnimation) && state.state.surface) {
             if (const auto layer = fromHandle(state.state.surface).promote()) {
@@ -3925,7 +4072,7 @@
 
     transactionFlags |= clientStateFlags;
 
-    if (permissions & Permission::ACCESS_SURFACE_FLINGER) {
+    if (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) {
         transactionFlags |= addInputWindowCommands(inputWindowCommands);
     } else if (!inputWindowCommands.empty()) {
         ALOGE("Only privileged callers are allowed to send input commands.");
@@ -4036,11 +4183,11 @@
 }
 
 uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
-                                              const ComposerState& composerState,
+                                              ComposerState& composerState,
                                               int64_t desiredPresentTime, bool isAutoTimestamp,
                                               int64_t postTime, uint32_t permissions) {
-    const layer_state_t& s = composerState.state;
-    const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
+    layer_state_t& s = composerState.state;
+    s.sanitize(permissions);
 
     std::vector<ListenerCallbacks> filteredListeners;
     for (auto& listener : s.listeners) {
@@ -4064,15 +4211,7 @@
     uint32_t flags = 0;
     sp<Layer> layer = nullptr;
     if (s.surface) {
-        if (what & layer_state_t::eLayerCreated) {
-            layer = handleLayerCreatedLocked(s.surface);
-            if (layer) {
-                flags |= eTransactionNeeded | eTraversalNeeded;
-                mLayersAdded = true;
-            }
-        } else {
-            layer = fromHandle(s.surface).promote();
-        }
+        layer = fromHandle(s.surface).promote();
     } else {
         // The client may provide us a null handle. Treat it as if the layer was removed.
         ALOGW("Attempt to set client state with a null layer handle");
@@ -4158,43 +4297,14 @@
         }
     }
     if (what & layer_state_t::eMatrixChanged) {
-        // TODO: b/109894387
-        //
-        // SurfaceFlinger's renderer is not prepared to handle cropping in the face of arbitrary
-        // rotation. To see the problem observe that if we have a square parent, and a child
-        // of the same size, then we rotate the child 45 degrees around it's center, the child
-        // must now be cropped to a non rectangular 8 sided region.
-        //
-        // Of course we can fix this in the future. For now, we are lucky, SurfaceControl is
-        // private API, and arbitrary rotation is used in limited use cases, for instance:
-        // - WindowManager only uses rotation in one case, which is on a top level layer in which
-        //   cropping is not an issue.
-        // - Launcher, as a privileged app, uses this to transition an application to PiP
-        //   (picture-in-picture) mode.
-        //
-        // However given that abuse of rotation matrices could lead to surfaces extending outside
-        // of cropped areas, we need to prevent non-root clients without permission
-        // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER
-        // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle
-        // preserving transformations.
-        const bool allowNonRectPreservingTransforms =
-                permissions & Permission::ROTATE_SURFACE_FLINGER;
-        if (layer->setMatrix(s.matrix, allowNonRectPreservingTransforms)) flags |= eTraversalNeeded;
+        if (layer->setMatrix(s.matrix)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eTransparentRegionChanged) {
         if (layer->setTransparentRegionHint(s.transparentRegion))
             flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eFlagsChanged) {
-        auto changedFlags = s.flags;
-        if (changedFlags & layer_state_t::eLayerIsDisplayDecoration) {
-            if ((permissions & Permission::INTERNAL_SYSTEM_WINDOW) == 0) {
-                changedFlags &= ~layer_state_t::eLayerIsDisplayDecoration;
-                ALOGE("Attempt to use eLayerIsDisplayDecoration without permission "
-                      "INTERNAL_SYSTEM_WINDOW!");
-            }
-        }
-        if (layer->setFlags(changedFlags, s.mask)) flags |= eTraversalNeeded;
+        if (layer->setFlags(s.flags, s.mask)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eCornerRadiusChanged) {
         if (layer->setCornerRadius(s.cornerRadius))
@@ -4252,12 +4362,8 @@
         if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eInputInfoChanged) {
-        if (privileged) {
-            layer->setInputInfo(*s.windowInfoHandle->getInfo());
-            flags |= eTraversalNeeded;
-        } else {
-            ALOGE("Attempt to update WindowInfo without permission ACCESS_SURFACE_FLINGER");
-        }
+        layer->setInputInfo(*s.windowInfoHandle->getInfo());
+        flags |= eTraversalNeeded;
     }
     std::optional<nsecs_t> dequeueBufferTimestamp;
     if (what & layer_state_t::eMetadataChanged) {
@@ -4281,22 +4387,19 @@
         if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eFrameRateSelectionPriority) {
-        if (privileged && layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
+        if (layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
             flags |= eTraversalNeeded;
         }
     }
     if (what & layer_state_t::eFrameRateChanged) {
-        if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility, s.changeFrameRateStrategy,
-                              "SurfaceFlinger::setClientStateLocked", privileged)) {
-            const auto compatibility =
-                    Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
-            const auto strategy =
-                    Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
+        const auto compatibility =
+            Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
+        const auto strategy =
+            Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
 
-            if (layer->setFrameRate(
-                        Layer::FrameRate(Fps::fromValue(s.frameRate), compatibility, strategy))) {
-                flags |= eTraversalNeeded;
-            }
+        if (layer->setFrameRate(
+                Layer::FrameRate(Fps::fromValue(s.frameRate), compatibility, strategy))) {
+          flags |= eTraversalNeeded;
         }
     }
     if (what & layer_state_t::eFixedTransformHintChanged) {
@@ -4308,12 +4411,8 @@
         layer->setAutoRefresh(s.autoRefresh);
     }
     if (what & layer_state_t::eTrustedOverlayChanged) {
-        if (privileged) {
-            if (layer->setTrustedOverlay(s.isTrustedOverlay)) {
-                flags |= eTraversalNeeded;
-            }
-        } else {
-            ALOGE("Attempt to set trusted overlay without permission ACCESS_SURFACE_FLINGER");
+        if (layer->setTrustedOverlay(s.isTrustedOverlay)) {
+            flags |= eTraversalNeeded;
         }
     }
     if (what & layer_state_t::eStretchChanged) {
@@ -4332,13 +4431,9 @@
         }
     }
     if (what & layer_state_t::eDropInputModeChanged) {
-        if (privileged) {
-            if (layer->setDropInputMode(s.dropInputMode)) {
-                flags |= eTraversalNeeded;
-                mInputInfoChanged = true;
-            }
-        } else {
-            ALOGE("Attempt to update DropInputMode without permission ACCESS_SURFACE_FLINGER");
+        if (layer->setDropInputMode(s.dropInputMode)) {
+            flags |= eTraversalNeeded;
+            mInputInfoChanged = true;
         }
     }
     // This has to happen after we reparent children because when we reparent to null we remove
@@ -4365,10 +4460,13 @@
         }
     }
 
-    if (what & layer_state_t::eBufferChanged &&
-        layer->setBuffer(s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
-                         dequeueBufferTimestamp, frameTimelineInfo)) {
-        flags |= eTraversalNeeded;
+    if (what & layer_state_t::eBufferChanged) {
+        std::shared_ptr<renderengine::ExternalTexture> buffer =
+                getExternalTextureFromBufferData(*s.bufferData, layer->getDebugName());
+        if (layer->setBuffer(buffer, *s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
+                             dequeueBufferTimestamp, frameTimelineInfo)) {
+            flags |= eTraversalNeeded;
+        }
     } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
         layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
     }
@@ -4408,12 +4506,12 @@
     }
 
     *outLayerId = mirrorLayer->sequence;
-    if (mTransactionTracingEnabled) {
-        mTransactionTracing.onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
-                                               args.name, mirrorFrom->sequence);
+    if (mTransactionTracing) {
+        mTransactionTracing->onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
+                                                args.name, mirrorFrom->sequence);
     }
     return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */,
-                          false /* addAsRoot */, nullptr /* outTransformHint */);
+                          false /* addToRoot */, nullptr /* outTransformHint */);
 }
 
 status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
@@ -4453,7 +4551,7 @@
         return result;
     }
 
-    bool addToRoot = callingThreadHasUnscopedSurfaceFlingerAccess();
+    bool addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
     wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer);
     if (parentHandle != nullptr && parent == nullptr) {
         ALOGE("Invalid parent handle %p.", parentHandle.get());
@@ -4470,9 +4568,9 @@
     if (parentSp != nullptr) {
         parentId = parentSp->getSequence();
     }
-    if (mTransactionTracingEnabled) {
-        mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
-                                         args.flags, parentId);
+    if (mTransactionTracing) {
+        mTransactionTracing->onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
+                                          args.flags, parentId);
     }
 
     result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint);
@@ -4480,7 +4578,6 @@
         return result;
     }
 
-    setTransactionFlags(eTransactionNeeded);
     *outLayerId = layer->sequence;
     return result;
 }
@@ -4562,8 +4659,8 @@
     markLayerPendingRemovalLocked(layer);
     mBufferCountTracker.remove(handle);
     layer.clear();
-    if (mTransactionTracingEnabled) {
-        mTransactionTracing.onHandleRemoved(handle);
+    if (mTransactionTracing) {
+        mTransactionTracing->onHandleRemoved(handle);
     }
 }
 
@@ -4798,7 +4895,9 @@
 status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
     if (asProto) {
         mLayerTracing.writeToFile();
-        mTransactionTracing.writeToFile();
+        if (mTransactionTracing) {
+            mTransactionTracing->writeToFile();
+        }
     }
 
     return doDump(fd, DumpArgs(), asProto);
@@ -5195,9 +5294,15 @@
      * Tracing state
      */
     mLayerTracing.dump(result);
-    result.append("\n");
-    mTransactionTracing.dump(result);
-    result.append("\n");
+
+    result.append("\nTransaction tracing: ");
+    if (mTransactionTracing) {
+        result.append("enabled\n");
+        mTransactionTracing->dump(result);
+    } else {
+        result.append("disabled\n");
+    }
+    result.push_back('\n');
 
     /*
      * HWC layer minidump
@@ -5294,6 +5399,10 @@
         case SET_DESIRED_DISPLAY_MODE_SPECS:
         case GET_DESIRED_DISPLAY_MODE_SPECS:
         case SET_ACTIVE_COLOR_MODE:
+        case GET_BOOT_DISPLAY_MODE_SUPPORT:
+        case SET_BOOT_DISPLAY_MODE:
+        case CLEAR_BOOT_DISPLAY_MODE:
+        case GET_PREFERRED_BOOT_DISPLAY_MODE:
         case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
         case SET_AUTO_LOW_LATENCY_MODE:
         case GET_GAME_CONTENT_TYPE_SUPPORT:
@@ -5359,6 +5468,7 @@
         // special permissions.
         case SET_FRAME_RATE:
         case GET_DISPLAY_BRIGHTNESS_SUPPORT:
+        case GET_DISPLAY_DECORATION_SUPPORT:
         // captureLayers and captureDisplay will handle the permission check in the function
         case CAPTURE_LAYERS:
         case CAPTURE_DISPLAY:
@@ -5405,6 +5515,7 @@
             }
             return PERMISSION_DENIED;
         }
+        case SET_OVERRIDE_FRAME_RATE:
         case ON_PULL_ATOM: {
             const int uid = IPCThreadState::self()->getCallingUid();
             if (uid == AID_SYSTEM) {
@@ -5430,9 +5541,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1041 are currently used for backdoors. The code
+    // Numbers from 1000 to 1042 are currently used for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1041) {
+    if (code >= 1000 && code <= 1042) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5619,12 +5730,18 @@
             }
             case 1025: { // Set layer tracing
                 n = data.readInt32();
+                int64_t fixedStartingTime = data.readInt64();
                 bool tracingEnabledChanged;
                 if (n) {
                     ALOGD("LayerTracing enabled");
                     tracingEnabledChanged = mLayerTracing.enable();
                     if (tracingEnabledChanged) {
-                        mScheduler->schedule([&]() MAIN_THREAD { mLayerTracing.notify("start"); })
+                        int64_t startingTime =
+                                (fixedStartingTime) ? fixedStartingTime : systemTime();
+                        mScheduler
+                                ->schedule([&]() MAIN_THREAD {
+                                    mLayerTracing.notify("start", startingTime);
+                                })
                                 .wait();
                     }
                 } else {
@@ -5872,15 +5989,27 @@
                 return NO_ERROR;
             }
             case 1041: { // Transaction tracing
-                if (data.readInt32()) {
-                    // Transaction tracing is always running but allow the user to temporarily
-                    // increase the buffer when actively debugging.
-                    mTransactionTracing.setBufferSize(
-                            TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
-                } else {
-                    mTransactionTracing.setBufferSize(
-                            TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
-                    mTransactionTracing.writeToFile();
+                if (mTransactionTracing) {
+                    if (data.readInt32()) {
+                        // Transaction tracing is always running but allow the user to temporarily
+                        // increase the buffer when actively debugging.
+                        mTransactionTracing->setBufferSize(
+                                TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
+                    } else {
+                        mTransactionTracing->setBufferSize(
+                                TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
+                        mTransactionTracing->writeToFile();
+                    }
+                }
+                reply->writeInt32(NO_ERROR);
+                return NO_ERROR;
+            }
+            case 1042: { // Write layers trace or transaction trace to file
+                if (mTransactionTracing) {
+                    mTransactionTracing->writeToFile();
+                }
+                if (mLayerTracingEnabled) {
+                    mLayerTracing.writeToFile();
                 }
                 reply->writeInt32(NO_ERROR);
                 return NO_ERROR;
@@ -6329,9 +6458,10 @@
     const status_t bufferStatus = buffer->initCheck();
     LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d",
                         bufferStatus);
-    const auto texture = std::make_shared<
-            renderengine::ExternalTexture>(buffer, getRenderEngine(),
-                                           renderengine::ExternalTexture::Usage::WRITEABLE);
+    const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
+            renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
+                                                 renderengine::impl::ExternalTexture::Usage::
+                                                         WRITEABLE);
     return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, texture,
                                false /* regionSampling */, grayscale, captureListener);
 }
@@ -6408,7 +6538,7 @@
                 captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
     });
 
-    const bool useProtected = buffer->getBuffer()->getUsage() & GRALLOC_USAGE_PROTECTED;
+    const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
 
     // We allow the system server to take screenshots of secure layers for
     // use in situations like the Screen-rotation animation and place
@@ -6721,8 +6851,8 @@
     if (!layer->isRemovedFromCurrentState()) {
         mScheduler->deregisterLayer(layer);
     }
-    if (mTransactionTracingEnabled) {
-        mTransactionTracing.onLayerRemoved(layer->getSequence());
+    if (mTransactionTracing) {
+        mTransactionTracing->onLayerRemoved(layer->getSequence());
     }
 }
 
@@ -6809,6 +6939,17 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::setOverrideFrameRate(uid_t uid, float frameRate) {
+    PhysicalDisplayId displayId = [&]() {
+        Mutex::Autolock lock(mStateLock);
+        return getDefaultDisplayDeviceLocked()->getPhysicalId();
+    }();
+
+    mScheduler->setGameModeRefreshRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
+    mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
                                               const FrameTimelineInfo& frameTimelineInfo) {
     Mutex::Autolock lock(mStateLock);
@@ -6901,53 +7042,19 @@
     }
 }
 
-void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
-                                          const wp<Layer> parent, bool addToRoot) {
-    Mutex::Autolock lock(mCreatedLayersLock);
-    mCreatedLayers[handle->localBinder()] =
-            std::make_unique<LayerCreatedState>(layer, parent, addToRoot);
-}
-
-auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
-    Mutex::Autolock lock(mCreatedLayersLock);
-    BBinder* b = nullptr;
-    if (handle) {
-        b = handle->localBinder();
-    }
-
-    if (b == nullptr) {
-        return std::unique_ptr<LayerCreatedState>(nullptr);
-    }
-
-    auto it = mCreatedLayers.find(b);
-    if (it == mCreatedLayers.end()) {
-        ALOGE("Can't find layer from handle %p", handle.get());
-        return std::unique_ptr<LayerCreatedState>(nullptr);
-    }
-
-    auto state = std::move(it->second);
-    mCreatedLayers.erase(it);
-    return state;
-}
-
-sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) {
-    const auto& state = getLayerCreatedState(handle);
-    if (!state) {
-        return nullptr;
-    }
-
-    sp<Layer> layer = state->layer.promote();
+void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state) {
+    sp<Layer> layer = state.layer.promote();
     if (!layer) {
-        ALOGE("Invalid layer %p", state->layer.unsafe_get());
-        return nullptr;
+        ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get());
+        return;
     }
 
     sp<Layer> parent;
-    bool addToRoot = state->addToRoot;
-    if (state->initialParent != nullptr) {
-        parent = state->initialParent.promote();
+    bool addToRoot = state.addToRoot;
+    if (state.initialParent != nullptr) {
+        parent = state.initialParent.promote();
         if (parent == nullptr) {
-            ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
+            ALOGD("Parent was destroyed soon after creation %p", state.initialParent.unsafe_get());
             addToRoot = false;
         }
     }
@@ -6967,7 +7074,6 @@
     layer->updateTransformHint(mActiveDisplayTransformHint);
 
     mInterceptor->saveSurfaceCreation(layer);
-    return layer;
 }
 
 void SurfaceFlinger::sample() {
@@ -7019,6 +7125,56 @@
     return NO_ERROR;
 }
 
+std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
+        const BufferData& bufferData, const char* layerName) const {
+    bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
+    bool bufferSizeExceedsLimit = false;
+    std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+    if (cacheIdChanged && bufferData.buffer != nullptr) {
+        bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
+                                                            bufferData.buffer->getHeight());
+        if (!bufferSizeExceedsLimit) {
+            ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
+            buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
+        }
+    } else if (cacheIdChanged) {
+        buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
+    } else if (bufferData.buffer != nullptr) {
+        bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
+                                                            bufferData.buffer->getHeight());
+        if (!bufferSizeExceedsLimit) {
+            buffer = std::make_shared<
+                    renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(),
+                                                         renderengine::impl::ExternalTexture::
+                                                                 Usage::READABLE);
+        }
+    }
+    ALOGE_IF(bufferSizeExceedsLimit,
+             "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
+             "limit.",
+             layerName);
+    return buffer;
+}
+
+bool SurfaceFlinger::commitCreatedLayers() {
+    std::vector<LayerCreatedState> createdLayers;
+    {
+        std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+        createdLayers = std::move(mCreatedLayers);
+        mCreatedLayers.clear();
+        if (createdLayers.size() == 0) {
+            return false;
+        }
+    }
+
+    Mutex::Autolock _l(mStateLock);
+    for (const auto& createdLayer : createdLayers) {
+        handleLayerCreatedLocked(createdLayer);
+    }
+    createdLayers.clear();
+    mLayersAdded = true;
+    return true;
+}
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e1b52c5..97a32d4 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -252,10 +252,6 @@
 
     static constexpr SkipInitializationTag SkipInitialization;
 
-    // Whether or not SDR layers should be dimmed to the desired SDR white point instead of
-    // being treated as native display brightness
-    static bool enableSdrDimming;
-
     static LatchUnsignaledConfig enableLatchUnsignaledConfig;
 
     // must be called before clients can connect
@@ -330,6 +326,9 @@
     virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
             REQUIRES(mStateLock);
 
+    virtual std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
+            const BufferData& bufferData, const char* layerName) const;
+
     // Returns true if any display matches a `bool(const DisplayDevice&)` predicate.
     template <typename Predicate>
     bool hasDisplay(Predicate p) const REQUIRES(mStateLock) {
@@ -535,6 +534,11 @@
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
                                        ui::DisplayPrimaries&) override;
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+    status_t getBootDisplayModeSupport(bool* outSupport) const override;
+    status_t setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) override;
+    status_t clearBootDisplayMode(const sp<IBinder>& displayToken) override;
+    status_t getPreferredBootDisplayMode(const sp<IBinder>& displayToken,
+                                         ui::DisplayModeId* id) override;
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
     void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
     void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
@@ -594,12 +598,16 @@
     status_t notifyPowerBoost(int32_t boostId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
+    status_t getDisplayDecorationSupport(const sp<IBinder>& displayToken,
+                                         bool* outSupport) const override;
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                           int8_t compatibility, int8_t changeFrameRateStrategy) override;
 
     status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
                                   const FrameTimelineInfo& frameTimelineInfo) override;
 
+    status_t setOverrideFrameRate(uid_t uid, float frameRate) override;
+
     status_t addTransactionTraceListener(
             const sp<gui::ITransactionTraceListener>& listener) override;
 
@@ -674,6 +682,9 @@
     void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
             REQUIRES(mStateLock);
 
+    // Returns true if the display has a visible HDR layer in its layer stack.
+    bool hasVisibleHdrLayer(const sp<DisplayDevice>& display) REQUIRES(mStateLock);
+
     // Sets the desired display mode specs.
     status_t setDesiredDisplayModeSpecsInternal(
             const sp<DisplayDevice>& display,
@@ -690,6 +701,7 @@
     void updateLayerGeometry();
 
     void updateInputFlinger();
+    void persistDisplayBrightness(bool needsComposite) REQUIRES(SF_MAIN_THREAD);
     void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
                           std::vector<gui::DisplayInfo>& outDisplayInfos);
     void commitInputWindowCommands() REQUIRES(mStateLock);
@@ -717,7 +729,7 @@
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
 
-    uint32_t setClientStateLocked(const FrameTimelineInfo&, const ComposerState&,
+    uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
                                   int64_t desiredPresentTime, bool isAutoTimestamp,
                                   int64_t postTime, uint32_t permissions) REQUIRES(mStateLock);
 
@@ -1187,8 +1199,7 @@
     LayerTracing mLayerTracing{*this};
     bool mLayerTracingEnabled = false;
 
-    TransactionTracing mTransactionTracing;
-    bool mTransactionTracingEnabled = false;
+    std::optional<TransactionTracing> mTransactionTracing;
     std::atomic<bool> mTracingEnabledChanged = false;
 
     const std::shared_ptr<TimeStats> mTimeStats;
@@ -1316,7 +1327,7 @@
 
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
             GUARDED_BY(mStateLock);
-    mutable Mutex mCreatedLayersLock;
+    mutable std::mutex mCreatedLayersLock;
     struct LayerCreatedState {
         LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot)
               : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
@@ -1332,11 +1343,9 @@
 
     // A temporay pool that store the created layers and will be added to current state in main
     // thread.
-    std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
-    void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
-                              const wp<Layer> parent, bool addToRoot);
-    auto getLayerCreatedState(const sp<IBinder>& handle);
-    sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
+    std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock);
+    bool commitCreatedLayers();
+    void handleLayerCreatedLocked(const LayerCreatedState& state) REQUIRES(mStateLock);
 
     std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
 
@@ -1357,6 +1366,13 @@
     float getLayerFramerate(nsecs_t now, int32_t id) const {
         return mScheduler->getLayerFramerate(now, id);
     }
+
+    struct {
+        bool sessionEnabled = false;
+        nsecs_t commitStart;
+        nsecs_t compositeStart;
+        nsecs_t presentEnd;
+    } mPowerHintSessionData GUARDED_BY(SF_MAIN_THREAD);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index eee4bec..558b3be 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -15,11 +15,14 @@
  */
 
 #pragma once
-#include <android-base/stringprintf.h>
+
+#include <chrono>
+#include <cmath>
+#include <functional>
+#include <string>
+
 #include <cutils/compiler.h>
 #include <utils/Trace.h>
-#include <cmath>
-#include <string>
 
 namespace std {
 template <class Rep, class Period>
@@ -75,7 +78,7 @@
         }
 
         if (mNameNegative.empty()) {
-            mNameNegative = base::StringPrintf("%sNegative", mName.c_str());
+            mNameNegative = mName + "Negative";
         }
 
         if (!std::signbit(mData)) {
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index d136e0b..006efdf 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -98,16 +98,20 @@
     mBuffer->dump(result);
 }
 
-void LayerTracing::notify(const char* where) {
-    ATRACE_CALL();
+void LayerTracing::notify(bool visibleRegionDirty, int64_t time) {
     std::scoped_lock lock(mTraceLock);
     if (!mEnabled) {
         return;
     }
 
+    if (!visibleRegionDirty && !flagIsSet(LayerTracing::TRACE_BUFFERS)) {
+        return;
+    }
+
     ATRACE_CALL();
     LayersTraceProto entry;
-    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+    entry.set_elapsed_realtime_nanos(time);
+    const char* where = visibleRegionDirty ? "visibleRegionsDirty" : "bufferLatched";
     entry.set_where(where);
     LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags));
 
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index 8ca3587..bd448c9 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -47,7 +47,7 @@
     bool isEnabled() const;
     status_t writeToFile();
     LayersTraceFileProto createTraceFileProto() const;
-    void notify(const char* where);
+    void notify(bool visibleRegionDirty, int64_t time);
 
     enum : uint32_t {
         TRACE_INPUT = 1 << 1,
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 378deb0..a91698f 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -31,6 +31,7 @@
     proto.set_vsync_id(t.frameTimelineInfo.vsyncId);
     proto.set_input_event_id(t.frameTimelineInfo.inputEventId);
     proto.set_post_time(t.postTime);
+    proto.set_transaction_id(t.id);
 
     for (auto& layerState : t.states) {
         proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state, getLayerId)));
@@ -52,6 +53,9 @@
             bufferProto->set_buffer_id(state.bufferId);
             bufferProto->set_width(state.bufferWidth);
             bufferProto->set_height(state.bufferHeight);
+            bufferProto->set_pixel_format(
+                    static_cast<proto::LayerState_BufferData_PixelFormat>(state.pixelFormat));
+            bufferProto->set_usage(state.bufferUsage);
         }
         layerProto.set_has_sideband_stream(state.hasSidebandStream);
         layerProto.set_layer_id(state.layerId);
@@ -132,14 +136,17 @@
     }
     if (layer.what & layer_state_t::eBufferChanged) {
         proto::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
-        if (layer.bufferData.buffer) {
-            bufferProto->set_buffer_id(layer.bufferData.buffer->getId());
-            bufferProto->set_width(layer.bufferData.buffer->getWidth());
-            bufferProto->set_height(layer.bufferData.buffer->getHeight());
+        if (layer.bufferData->buffer) {
+            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());
         }
-        bufferProto->set_frame_number(layer.bufferData.frameNumber);
-        bufferProto->set_flags(layer.bufferData.flags.get());
-        bufferProto->set_cached_buffer_id(layer.bufferData.cachedBuffer.id);
+        bufferProto->set_frame_number(layer.bufferData->frameNumber);
+        bufferProto->set_flags(layer.bufferData->flags.get());
+        bufferProto->set_cached_buffer_id(layer.bufferData->cachedBuffer.id);
     }
     if (layer.what & layer_state_t::eSidebandStreamChanged) {
         proto.set_has_sideband_stream(layer.sidebandStream != nullptr);
@@ -169,6 +176,7 @@
                 ? getLayerId(layer.relativeLayerSurfaceControl->getHandle())
                 : -1;
         proto.set_relative_parent_id(layerId);
+        proto.set_z(layer.z);
     }
 
     if (layer.what & layer_state_t::eInputInfoChanged) {
@@ -291,10 +299,13 @@
     t.frameTimelineInfo.vsyncId = proto.vsync_id();
     t.frameTimelineInfo.inputEventId = proto.input_event_id();
     t.postTime = proto.post_time();
+    t.id = proto.transaction_id();
+
     int32_t layerCount = proto.layer_changes_size();
     t.states.reserve(static_cast<size_t>(layerCount));
     for (int i = 0; i < layerCount; i++) {
         ComposerState s;
+        s.state.what = 0;
         fromProto(proto.layer_changes(i), getLayerHandle, s.state);
         t.states.add(s);
     }
@@ -316,27 +327,31 @@
     outArgs.mirrorFromId = proto.mirror_from_id();
 }
 
-void TransactionProtoParser::fromProto(const proto::LayerState& proto,
-                                       LayerIdToHandleFn getLayerHandle,
-                                       TracingLayerState& outState) {
-    fromProto(proto, getLayerHandle, static_cast<layer_state_t&>(outState));
-    if (proto.what() & layer_state_t::eReparent) {
+void TransactionProtoParser::mergeFromProto(const proto::LayerState& proto,
+                                            LayerIdToHandleFn getLayerHandle,
+                                            TracingLayerState& outState) {
+    layer_state_t state;
+    fromProto(proto, getLayerHandle, state);
+    outState.merge(state);
+
+    if (state.what & layer_state_t::eReparent) {
         outState.parentId = proto.parent_id();
-        outState.args.parentId = outState.parentId;
     }
-    if (proto.what() & layer_state_t::eRelativeLayerChanged) {
+    if (state.what & layer_state_t::eRelativeLayerChanged) {
         outState.relativeParentId = proto.relative_parent_id();
     }
-    if (proto.what() & layer_state_t::eInputInfoChanged) {
+    if (state.what & layer_state_t::eInputInfoChanged) {
         outState.inputCropId = proto.window_info_handle().crop_layer_id();
     }
-    if (proto.what() & layer_state_t::eBufferChanged) {
+    if (state.what & layer_state_t::eBufferChanged) {
         const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
         outState.bufferId = bufferProto.buffer_id();
         outState.bufferWidth = bufferProto.width();
         outState.bufferHeight = bufferProto.height();
+        outState.pixelFormat = bufferProto.pixel_format();
+        outState.bufferUsage = bufferProto.usage();
     }
-    if (proto.what() & layer_state_t::eSidebandStreamChanged) {
+    if (state.what & layer_state_t::eSidebandStreamChanged) {
         outState.hasSidebandStream = proto.has_sideband_stream();
     }
 }
@@ -405,10 +420,13 @@
         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.frameNumber = bufferProto.frame_number();
-        layer.bufferData.flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
-        layer.bufferData.cachedBuffer.id = bufferProto.cached_buffer_id();
+        layer.bufferData->frameNumber = bufferProto.frame_number();
+        layer.bufferData->flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
+        layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
     }
 
     if (proto.what() & layer_state_t::eApiChanged) {
@@ -429,15 +447,24 @@
 
     if ((proto.what() & layer_state_t::eReparent) && (getLayerHandle != nullptr)) {
         int32_t layerId = proto.parent_id();
-        layer.parentSurfaceControlForChild =
-                new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
-                                   nullptr, layerId);
+        if (layerId == -1) {
+            layer.parentSurfaceControlForChild = nullptr;
+        } else {
+            layer.parentSurfaceControlForChild =
+                    new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+                                       nullptr, layerId);
+        }
     }
-    if ((proto.what() & layer_state_t::eRelativeLayerChanged) && (getLayerHandle != nullptr)) {
+    if (proto.what() & layer_state_t::eRelativeLayerChanged) {
         int32_t layerId = proto.relative_parent_id();
-        layer.relativeLayerSurfaceControl =
-                new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
-                                   nullptr, layerId);
+        if (layerId == -1) {
+            layer.relativeLayerSurfaceControl = nullptr;
+        } else if (getLayerHandle != nullptr) {
+            layer.relativeLayerSurfaceControl =
+                    new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+                                       nullptr, layerId);
+        }
+        layer.z = proto.z();
     }
 
     if ((proto.what() & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) {
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index b78d3d9..d589936 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -34,6 +34,8 @@
     uint64_t bufferId;
     uint32_t bufferHeight;
     uint32_t bufferWidth;
+    int32_t pixelFormat;
+    uint64_t bufferUsage;
     bool hasSidebandStream;
     int32_t parentId;
     int32_t relativeParentId;
@@ -58,8 +60,8 @@
     static TransactionState fromProto(const proto::TransactionState&,
                                       LayerIdToHandleFn getLayerHandleFn,
                                       DisplayIdToHandleFn getDisplayHandleFn);
-    static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn,
-                          TracingLayerState& outState);
+    static void mergeFromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn,
+                               TracingLayerState& outState);
     static void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
 
 private:
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index b5966d5..a46b795 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -23,35 +23,22 @@
 #include <utils/SystemClock.h>
 #include <utils/Trace.h>
 
-#include "RingBuffer.h"
 #include "TransactionTracing.h"
 
 namespace android {
 
 TransactionTracing::TransactionTracing() {
-    mBuffer = std::make_unique<
-            RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>>();
-}
-
-TransactionTracing::~TransactionTracing() = default;
-
-bool TransactionTracing::enable() {
     std::scoped_lock lock(mTraceLock);
-    if (mEnabled) {
-        return false;
-    }
-    mBuffer->setSize(mBufferSizeInBytes);
+
+    mBuffer.setSize(mBufferSizeInBytes);
     mStartingTimestamp = systemTime();
-    mEnabled = true;
     {
         std::scoped_lock lock(mMainThreadLock);
-        mDone = false;
         mThread = std::thread(&TransactionTracing::loop, this);
     }
-    return true;
 }
 
-bool TransactionTracing::disable() {
+TransactionTracing::~TransactionTracing() {
     std::thread thread;
     {
         std::scoped_lock lock(mMainThreadLock);
@@ -63,43 +50,20 @@
         thread.join();
     }
 
-    std::scoped_lock lock(mTraceLock);
-    if (!mEnabled) {
-        return false;
-    }
-    mEnabled = false;
-
-    writeToFileLocked();
-    mBuffer->reset();
-    mQueuedTransactions.clear();
-    mStartingStates.clear();
-    mLayerHandles.clear();
-    return true;
-}
-
-bool TransactionTracing::isEnabled() const {
-    std::scoped_lock lock(mTraceLock);
-    return mEnabled;
+    writeToFile();
 }
 
 status_t TransactionTracing::writeToFile() {
     std::scoped_lock lock(mTraceLock);
-    if (!mEnabled) {
-        return STATUS_OK;
-    }
-    return writeToFileLocked();
-}
-
-status_t TransactionTracing::writeToFileLocked() {
     proto::TransactionTraceFile fileProto = createTraceFileProto();
     addStartingStateToProtoLocked(fileProto);
-    return mBuffer->writeToFile(fileProto, FILE_NAME);
+    return mBuffer.writeToFile(fileProto, FILE_NAME);
 }
 
 void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
     std::scoped_lock lock(mTraceLock);
     mBufferSizeInBytes = bufferSizeInBytes;
-    mBuffer->setSize(mBufferSizeInBytes);
+    mBuffer.setSize(mBufferSizeInBytes);
 }
 
 proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
@@ -111,21 +75,16 @@
 
 void TransactionTracing::dump(std::string& result) const {
     std::scoped_lock lock(mTraceLock);
-    base::StringAppendF(&result, "Transaction tracing state: %s\n",
-                        mEnabled ? "enabled" : "disabled");
     base::StringAppendF(&result,
                         "  queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
                         mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
                         mStartingStates.size());
-    mBuffer->dump(result);
+    mBuffer.dump(result);
 }
 
 void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
     std::scoped_lock lock(mTraceLock);
     ATRACE_CALL();
-    if (!mEnabled) {
-        return;
-    }
     mQueuedTransactions[transaction.id] =
             TransactionProtoParser::toProto(transaction,
                                             std::bind(&TransactionTracing::getLayerIdLocked, this,
@@ -169,7 +128,9 @@
             mCommittedTransactions.clear();
         } // unlock mMainThreadLock
 
-        addEntry(committedTransactions, removedLayers);
+        if (!committedTransactions.empty() || !removedLayers.empty()) {
+            addEntry(committedTransactions, removedLayers);
+        }
     }
 }
 
@@ -206,10 +167,17 @@
         std::string serializedProto;
         entryProto.SerializeToString(&serializedProto);
         entryProto.Clear();
-        std::vector<std::string> entries = mBuffer->emplace(std::move(serializedProto));
+        std::vector<std::string> entries = mBuffer.emplace(std::move(serializedProto));
         removedEntries.reserve(removedEntries.size() + entries.size());
         removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
                               std::make_move_iterator(entries.end()));
+
+        entryProto.mutable_removed_layer_handles()->Reserve(
+                static_cast<int32_t>(mRemovedLayerHandles.size()));
+        for (auto& handle : mRemovedLayerHandles) {
+            entryProto.mutable_removed_layer_handles()->Add(handle);
+        }
+        mRemovedLayerHandles.clear();
     }
 
     proto::TransactionTraceEntry removedEntryProto;
@@ -229,10 +197,10 @@
     base::ScopedLockAssertion assumeLocked(mTraceLock);
     mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
         proto::TransactionTraceEntry entry;
-        if (mBuffer->used() > 0) {
-            entry.ParseFromString(mBuffer->back());
+        if (mBuffer.used() > 0) {
+            entry.ParseFromString(mBuffer.back());
         }
-        return mBuffer->used() > 0 && entry.vsync_id() >= vsyncId;
+        return mBuffer.used() > 0 && entry.vsync_id() >= vsyncId;
     });
 }
 
@@ -267,7 +235,14 @@
 
 void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
     std::scoped_lock lock(mTraceLock);
-    mLayerHandles.erase(layerHandle);
+    auto it = mLayerHandles.find(layerHandle);
+    if (it == mLayerHandles.end()) {
+        ALOGW("handle not found. %p", layerHandle);
+        return;
+    }
+
+    mRemovedLayerHandles.push_back(it->second);
+    mLayerHandles.erase(it);
 }
 
 void TransactionTracing::tryPushToTracingThread() {
@@ -302,6 +277,7 @@
 
 void TransactionTracing::updateStartingStateLocked(
         const proto::TransactionTraceEntry& removedEntry) {
+    mStartingTimestamp = removedEntry.elapsed_realtime_nanos();
     // Keep track of layer starting state so we can reconstruct the layer state as we purge
     // transactions from the buffer.
     for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
@@ -318,7 +294,7 @@
                 ALOGW("Could not find layer id %d", layerState.layer_id());
                 continue;
             }
-            TransactionProtoParser::fromProto(layerState, nullptr, it->second);
+            TransactionProtoParser::mergeFromProto(layerState, nullptr, it->second);
         }
     }
 
@@ -330,13 +306,14 @@
 }
 
 void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
-    proto::TransactionTraceEntry* entryProto = proto.add_entry();
-    entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
-    entryProto->set_vsync_id(0);
     if (mStartingStates.size() == 0) {
         return;
     }
 
+    proto::TransactionTraceEntry* entryProto = proto.add_entry();
+    entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
+    entryProto->set_vsync_id(0);
+
     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));
@@ -352,7 +329,7 @@
     std::scoped_lock<std::mutex> lock(mTraceLock);
     proto::TransactionTraceFile proto = createTraceFileProto();
     addStartingStateToProtoLocked(proto);
-    mBuffer->writeToProto(proto);
+    mBuffer.writeToProto(proto);
     return proto;
 }
 
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 26a3758..d5d98ce 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -25,17 +25,16 @@
 #include <mutex>
 #include <thread>
 
+#include "RingBuffer.h"
 #include "TransactionProtoParser.h"
 
 using namespace android::surfaceflinger;
 
 namespace android {
 
-template <typename FileProto, typename EntryProto>
-class RingBuffer;
-
 class SurfaceFlinger;
 class TransactionTracingTest;
+
 /*
  * Records all committed transactions into a ring bufffer.
  *
@@ -54,10 +53,6 @@
     TransactionTracing();
     ~TransactionTracing();
 
-    bool enable();
-    bool disable();
-    bool isEnabled() const;
-
     void addQueuedTransaction(const TransactionState&);
     void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
     status_t writeToFile();
@@ -78,8 +73,7 @@
     static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
 
     mutable std::mutex mTraceLock;
-    bool mEnabled GUARDED_BY(mTraceLock) = false;
-    std::unique_ptr<RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>> mBuffer
+    RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer
             GUARDED_BY(mTraceLock);
     size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
     std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
@@ -88,6 +82,7 @@
     std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
     std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
             GUARDED_BY(mTraceLock);
+    std::vector<int32_t /* layerId */> mRemovedLayerHandles GUARDED_BY(mTraceLock);
     std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
 
     // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
@@ -116,7 +111,6 @@
     void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
     void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
     void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
-    status_t writeToFileLocked() REQUIRES(mTraceLock);
 
     // TEST
     // Wait until all the committed transactions for the specified vsync id are added to the buffer.
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index b93d127..72434e9 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -25,24 +25,21 @@
 using gui::IWindowInfosListener;
 using gui::WindowInfo;
 
-struct WindowInfosReportedListener : gui::BnWindowInfosReportedListener {
-    explicit WindowInfosReportedListener(std::function<void()> listenerCb)
-          : mListenerCb(listenerCb) {}
+struct WindowInfosListenerInvoker::WindowInfosReportedListener
+      : gui::BnWindowInfosReportedListener {
+    explicit WindowInfosReportedListener(WindowInfosListenerInvoker& invoker) : mInvoker(invoker) {}
 
     binder::Status onWindowInfosReported() override {
-        if (mListenerCb != nullptr) {
-            mListenerCb();
-        }
+        mInvoker.windowInfosReported();
         return binder::Status::ok();
     }
 
-    std::function<void()> mListenerCb;
+    WindowInfosListenerInvoker& mInvoker;
 };
 
-WindowInfosListenerInvoker::WindowInfosListenerInvoker(const sp<SurfaceFlinger>& sf) : mSf(sf) {
-    mWindowInfosReportedListener =
-            new WindowInfosReportedListener([&]() { windowInfosReported(); });
-}
+WindowInfosListenerInvoker::WindowInfosListenerInvoker(SurfaceFlinger& flinger)
+      : mFlinger(flinger),
+        mWindowInfosReportedListener(sp<WindowInfosReportedListener>::make(*this)) {}
 
 void WindowInfosListenerInvoker::addWindowInfosListener(
         const sp<IWindowInfosListener>& windowInfosListener) {
@@ -91,8 +88,8 @@
 void WindowInfosListenerInvoker::windowInfosReported() {
     mCallbacksPending--;
     if (mCallbacksPending == 0) {
-        mSf->windowInfosReported();
+        mFlinger.windowInfosReported();
     }
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index 4e08393..2eabf48 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -31,7 +31,8 @@
 
 class WindowInfosListenerInvoker : public IBinder::DeathRecipient {
 public:
-    WindowInfosListenerInvoker(const sp<SurfaceFlinger>& sf);
+    explicit WindowInfosListenerInvoker(SurfaceFlinger&);
+
     void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
     void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
 
@@ -42,13 +43,15 @@
     void binderDied(const wp<IBinder>& who) override;
 
 private:
+    struct WindowInfosReportedListener;
     void windowInfosReported();
 
-    const sp<SurfaceFlinger> mSf;
+    SurfaceFlinger& mFlinger;
     std::mutex mListenersMutex;
     std::unordered_map<wp<IBinder>, const sp<gui::IWindowInfosListener>, WpHash>
             mWindowInfosListeners GUARDED_BY(mListenersMutex);
     sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener;
     std::atomic<size_t> mCallbacksPending{0};
 };
-} // namespace android
\ No newline at end of file
+
+} // namespace android
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 4529905..2e9e659 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -155,6 +155,7 @@
   uint32 height = 2;
   uint32 stride = 3;
   int32 format = 4;
+  uint64 usage = 5;
 }
 
 message BarrierLayerProto {
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index e31b502..fcf4499 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -46,6 +46,7 @@
     repeated int32 removed_layers = 5;
     repeated DisplayState added_displays = 6;
     repeated int32 removed_displays = 7;
+    repeated int32 removed_layer_handles = 8;
 }
 
 message LayerCreationArgs {
@@ -62,8 +63,9 @@
     int64 vsync_id = 3;
     int32 input_event_id = 4;
     int64 post_time = 5;
-    repeated LayerState layer_changes = 6;
-    repeated DisplayState display_changes = 7;
+    uint64 transaction_id = 6;
+    repeated LayerState layer_changes = 7;
+    repeated DisplayState display_changes = 8;
 }
 
 // Keep insync with layer_state_t
@@ -78,28 +80,34 @@
         eLayerChanged = 0x00000002;
         eSizeChanged = 0x00000004;
         eAlphaChanged = 0x00000008;
+
         eMatrixChanged = 0x00000010;
         eTransparentRegionChanged = 0x00000020;
         eFlagsChanged = 0x00000040;
         eLayerStackChanged = 0x00000080;
+
         eReleaseBufferListenerChanged = 0x00000400;
         eShadowRadiusChanged = 0x00000800;
-        eLayerCreated = 0x00001000;
+
         eBufferCropChanged = 0x00002000;
         eRelativeLayerChanged = 0x00004000;
         eReparent = 0x00008000;
+
         eColorChanged = 0x00010000;
         eDestroySurface = 0x00020000;
         eTransformChanged = 0x00040000;
         eTransformToDisplayInverseChanged = 0x00080000;
+
         eCropChanged = 0x00100000;
         eBufferChanged = 0x00200000;
         eAcquireFenceChanged = 0x00400000;
         eDataspaceChanged = 0x00800000;
+
         eHdrMetadataChanged = 0x01000000;
         eSurfaceDamageRegionChanged = 0x02000000;
         eApiChanged = 0x04000000;
         eSidebandStreamChanged = 0x08000000;
+
         eColorTransformChanged = 0x10000000;
         eHasListenerCallbacksChanged = 0x20000000;
         eInputInfoChanged = 0x40000000;
@@ -139,6 +147,7 @@
         eLayerSkipScreenshot = 0x40;
         eLayerSecure = 0x80;
         eEnableBackpressure = 0x100;
+        eLayerIsDisplayDecoration = 0x200;
     };
     uint32 flags = 9;
     uint32 mask = 10;
@@ -181,6 +190,26 @@
         }
         uint32 flags = 5;
         uint64 cached_buffer_id = 6;
+
+        enum PixelFormat {
+            PIXEL_FORMAT_UNKNOWN = 0;
+            PIXEL_FORMAT_CUSTOM = -4;
+            PIXEL_FORMAT_TRANSLUCENT = -3;
+            PIXEL_FORMAT_TRANSPARENT = -2;
+            PIXEL_FORMAT_OPAQUE = -1;
+            PIXEL_FORMAT_RGBA_8888 = 1;
+            PIXEL_FORMAT_RGBX_8888 = 2;
+            PIXEL_FORMAT_RGB_888 = 3;
+            PIXEL_FORMAT_RGB_565 = 4;
+            PIXEL_FORMAT_BGRA_8888 = 5;
+            PIXEL_FORMAT_RGBA_5551 = 6;
+            PIXEL_FORMAT_RGBA_4444 = 7;
+            PIXEL_FORMAT_RGBA_FP16 = 22;
+            PIXEL_FORMAT_RGBA_1010102 = 43;
+            PIXEL_FORMAT_R_8 = 0x38;
+        }
+        PixelFormat pixel_format = 7;
+        uint64 usage = 8;
     }
     BufferData buffer_data = 22;
     int32 api = 23;
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index f6b0def..027a15e 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -85,7 +85,7 @@
                              sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id,
                              ReleaseBufferCallbackHelper& releaseCallback) {
         Transaction t;
-        t.setBuffer(layer, buffer, fence, id.framenumber, id, releaseCallback.getCallback());
+        t.setBuffer(layer, buffer, fence, id.framenumber, releaseCallback.getCallback());
         t.addTransactionCompletedCallback(callback.function, callback.getContext());
         t.apply();
     }
@@ -300,7 +300,7 @@
     nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     Transaction t;
-    t.setBuffer(layer, firstBuffer, std::nullopt, std::nullopt, firstBufferCallbackId,
+    t.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
                 releaseCallback->getCallback());
     t.addTransactionCompletedCallback(transactionCallback.function,
                                       transactionCallback.getContext());
@@ -316,7 +316,7 @@
     // Dropping frames in transaction queue emits a callback
     sp<GraphicBuffer> secondBuffer = getBuffer();
     ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
-    t.setBuffer(layer, secondBuffer, std::nullopt, std::nullopt, secondBufferCallbackId,
+    t.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
                 releaseCallback->getCallback());
     t.addTransactionCompletedCallback(transactionCallback.function,
                                       transactionCallback.getContext());
@@ -360,7 +360,7 @@
 
     Transaction transaction1;
     transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                           secondBufferCallbackId, releaseCallback->getCallback());
+                           releaseCallback->getCallback());
     transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext());
 
     // Set a different TransactionCompletedListener to mimic a second process
@@ -395,14 +395,14 @@
     // Create transaction with a buffer.
     Transaction transaction;
     transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
-                          firstBufferCallbackId, releaseCallback->getCallback());
+                          releaseCallback->getCallback());
 
     sp<GraphicBuffer> secondBuffer = getBuffer();
     ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
 
     // Call setBuffer on the same transaction with a different buffer.
     transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                          secondBufferCallbackId, releaseCallback->getCallback());
+                          releaseCallback->getCallback());
 
     ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
@@ -417,7 +417,7 @@
     // Create transaction with a buffer.
     Transaction transaction1;
     transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
-                           firstBufferCallbackId, releaseCallback->getCallback());
+                           releaseCallback->getCallback());
 
     sp<GraphicBuffer> secondBuffer = getBuffer();
     ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
@@ -425,7 +425,7 @@
     // Create a second transaction with a new buffer for the same layer.
     Transaction transaction2;
     transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                           secondBufferCallbackId, releaseCallback->getCallback());
+                           releaseCallback->getCallback());
 
     // merge transaction1 into transaction2 so ensure we get a proper buffer release callback.
     transaction1.merge(std::move(transaction2));
@@ -446,7 +446,7 @@
 
     Transaction transaction1;
     transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
-                           firstBufferCallbackId, releaseCallback->getCallback());
+                           releaseCallback->getCallback());
 
     // Sent a second buffer to allow the first buffer to get released.
     sp<GraphicBuffer> secondBuffer = getBuffer();
@@ -454,7 +454,7 @@
 
     Transaction transaction2;
     transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                           secondBufferCallbackId, releaseCallback->getCallback());
+                           releaseCallback->getCallback());
 
     // Set a different TransactionCompletedListener to mimic a second process
     TransactionCompletedListener::setInstance(secondCompletedListener);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index afee99d..48f18b9 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -69,11 +69,11 @@
         "CachingTest.cpp",
         "CompositionTest.cpp",
         "DispSyncSourceTest.cpp",
-        "DisplayIdentificationTest.cpp",
         "DisplayIdGeneratorTest.cpp",
         "DisplayTransactionTest.cpp",
         "DisplayDevice_GetBestColorModeTest.cpp",
         "DisplayDevice_InitiateModeChange.cpp",
+        "DisplayDevice_SetDisplayBrightnessTest.cpp",
         "DisplayDevice_SetProjectionTest.cpp",
         "EventThreadTest.cpp",
         "FlagManagerTest.cpp",
@@ -100,7 +100,6 @@
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
         "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
         "SchedulerTest.cpp",
-        "SchedulerUtilsTest.cpp",
         "SetFrameRateTest.cpp",
         "RefreshRateConfigsTest.cpp",
         "RefreshRateSelectionTest.cpp",
@@ -108,7 +107,6 @@
         "RegionSamplingTest.cpp",
         "TimeStatsTest.cpp",
         "FrameTracerTest.cpp",
-        "TimerTest.cpp",
         "TransactionApplicationTest.cpp",
         "TransactionFrameTracerTest.cpp",
         "TransactionProtoParserTest.cpp",
@@ -185,11 +183,12 @@
         "server_configurable_flags",
     ],
     header_libs: [
+        "android.hardware.graphics.composer3-command-buffer",
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
         "android.hardware.graphics.composer@2.3-command-buffer",
         "android.hardware.graphics.composer@2.4-command-buffer",
-        "android.hardware.graphics.composer3-command-buffer",
+        "libscheduler_test_headers",
         "libsurfaceflinger_headers",
     ],
 }
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index f1e9b31..3716f59 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -30,6 +30,7 @@
 #include <gui/IProducerListener.h>
 #include <gui/LayerMetadata.h>
 #include <log/log.h>
+#include <renderengine/mock/FakeExternalTexture.h>
 #include <renderengine/mock/Framebuffer.h>
 #include <renderengine/mock/Image.h>
 #include <renderengine/mock/RenderEngine.h>
@@ -143,11 +144,10 @@
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
         EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
 
-        constexpr scheduler::ISchedulerCallback* kCallback = nullptr;
-        constexpr bool kHasMultipleConfigs = true;
         mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                                std::move(eventThread), std::move(sfEventThread), kCallback,
-                                kHasMultipleConfigs);
+                                std::move(eventThread), std::move(sfEventThread),
+                                TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
+                                TestableSurfaceFlinger::kTwoDisplayModes);
     }
 
     void setupForceGeometryDirty() {
@@ -234,15 +234,13 @@
                                                    CaptureArgs::UNSET_UID, visitor);
     };
 
-    // TODO: Eliminate expensive/real allocation if possible.
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-    mCaptureScreenBuffer = std::make_shared<
-            renderengine::ExternalTexture>(new GraphicBuffer(renderArea->getReqWidth(),
-                                                             renderArea->getReqHeight(),
-                                                             HAL_PIXEL_FORMAT_RGBA_8888, 1, usage,
-                                                             "screenshot"),
-                                           *mRenderEngine, true);
+    mCaptureScreenBuffer =
+            std::make_shared<renderengine::mock::FakeExternalTexture>(renderArea->getReqWidth(),
+                                                                      renderArea->getReqHeight(),
+                                                                      HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                                      usage);
 
     auto result = mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
                                                   forSystem, regionSampling);
@@ -621,7 +619,8 @@
             // TODO: use COLOR
             EXPECT_CALL(*test->mComposer,
                         setLayerColor(HWC_DISPLAY, HWC_LAYER,
-                                      IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
+                                      aidl::android::hardware::graphics::composer3::Color(
+                                              {1.0f, 1.0f, 1.0f, 1.0f})))
                     .Times(1);
         }
     }
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index a9ad249..f613e43 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -37,12 +37,11 @@
 
 class MockVSyncDispatch : public scheduler::VSyncDispatch {
 public:
-    MOCK_METHOD2(registerCallback,
-                 CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
-    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
-    MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
-    MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
-    MOCK_CONST_METHOD1(dump, void(std::string&));
+    MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
+    MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
+    MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
+    MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken), (override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
 
     MockVSyncDispatch() {
         ON_CALL(*this, registerCallback)
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 5a0033e..40a9b1a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -44,8 +44,8 @@
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
 
         mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
-                           .setSupportedModes({kDisplayMode60, kDisplayMode90, kDisplayMode120})
-                           .setActiveMode(kDisplayModeId60)
+                           .setDisplayModes({kDisplayMode60, kDisplayMode90, kDisplayMode120},
+                                            kDisplayModeId60)
                            .inject();
     }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
new file mode 100644
index 0000000..73c60e1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using hal::RenderIntent;
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class SetDisplayBrightnessTest : public DisplayTransactionTest {
+public:
+    sp<DisplayDevice> getDisplayDevice() { return injectDefaultInternalDisplay({}); }
+};
+
+TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessNoComposite) {
+    MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+    sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+    constexpr float kDisplayBrightness = 0.5f;
+    displayDevice->stageBrightness(kDisplayBrightness);
+
+    EXPECT_EQ(0.5f, displayDevice->getStagedBrightness());
+
+    displayDevice->persistBrightness(false);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
+TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithComposite) {
+    MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+    sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+    constexpr float kDisplayBrightness = 0.5f;
+    displayDevice->stageBrightness(kDisplayBrightness);
+
+    EXPECT_EQ(0.5f, displayDevice->getStagedBrightness());
+
+    displayDevice->persistBrightness(true);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(kDisplayBrightness,
+              displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
+TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithCompositeShortCircuitsOnNoOp) {
+    MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+    sp<DisplayDevice> displayDevice = getDisplayDevice();
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+
+    constexpr float kDisplayBrightness = 0.5f;
+    displayDevice->stageBrightness(kDisplayBrightness);
+
+    EXPECT_EQ(0.5f, displayDevice->getStagedBrightness());
+
+    displayDevice->persistBrightness(true);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(kDisplayBrightness,
+              displayDevice->getCompositionDisplay()->getState().displayBrightness);
+    displayDevice->getCompositionDisplay()->editState().displayBrightness = std::nullopt;
+
+    displayDevice->stageBrightness(kDisplayBrightness);
+    EXPECT_EQ(0.5f, displayDevice->getStagedBrightness());
+    displayDevice->persistBrightness(true);
+
+    EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness());
+    EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
deleted file mode 100644
index 1c8e5cc..0000000
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "DisplayHardware/DisplayIdentification.h"
-
-namespace android {
-
-const DisplayIdentificationData& getInternalEdid();
-const DisplayIdentificationData& getExternalEdid();
-const DisplayIdentificationData& getExternalEedid();
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayIdentificationTestHelpers.h
new file mode 100644
index 0000000..975bc12
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTestHelpers.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/DisplayIdentification.h>
+
+namespace android {
+
+template <size_t N>
+DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) {
+    return DisplayIdentificationData(bytes, bytes + N - 1);
+}
+
+inline const DisplayIdentificationData& getInternalEdid() {
+    static constexpr unsigned char kInternalEdid[] =
+            "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
+            "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
+            "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+            "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
+            "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
+            "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
+            "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
+            "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kInternalEdid);
+    return data;
+}
+
+inline const DisplayIdentificationData& getExternalEdid() {
+    static constexpr unsigned char kExternalEdid[] =
+            "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01"
+            "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
+            "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+            "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
+            "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
+            "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
+            "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff"
+            "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71";
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEdid);
+    return data;
+}
+
+inline const DisplayIdentificationData& getExternalEedid() {
+    // Extended EDID with timing extension.
+    static constexpr unsigned char kExternalEedid[] =
+            "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
+            "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
+            "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+            "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+            "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30"
+            "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+            "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+            "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d"
+            "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07"
+            "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01"
+            "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00"
+            "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00"
+            "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0"
+            "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kExternalEedid);
+    return data;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index b1f704a..2425862 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -60,6 +60,7 @@
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    mFlinger.resetScheduler(nullptr);
 }
 
 void DisplayTransactionTest::injectMockScheduler() {
@@ -76,7 +77,8 @@
     mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
                             std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
                             std::unique_ptr<EventThread>(mEventThread),
-                            std::unique_ptr<EventThread>(mSFEventThread), &mSchedulerCallback);
+                            std::unique_ptr<EventThread>(mSFEventThread),
+                            TestableSurfaceFlinger::SchedulerCallbackImpl::kMock);
 }
 
 void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 69ac26e..54b8bcb 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -22,7 +22,7 @@
 #pragma clang diagnostic ignored "-Wextra"
 
 #include <type_traits>
-#include "DisplayIdentificationTest.h"
+#include "DisplayIdentificationTestHelpers.h"
 
 #include <binder/IPCThreadState.h>
 #include <compositionengine/Display.h>
@@ -48,7 +48,6 @@
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSchedulerCallback.h"
 #include "mock/MockSurfaceInterceptor.h"
 #include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
@@ -121,7 +120,6 @@
 
     mock::VsyncController* mVsyncController = new mock::VsyncController;
     mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
-    scheduler::mock::SchedulerCallback mSchedulerCallback;
     mock::EventThread* mEventThread = new mock::EventThread;
     mock::EventThread* mSFEventThread = new mock::EventThread;
 
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 765dec3..2af0480 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -36,7 +36,7 @@
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/Hal.h"
-#include "DisplayIdentificationTest.h"
+#include "DisplayIdentificationTestHelpers.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockHWC2.h"
 
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index bd4dc59..1dd7dea 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -56,12 +56,11 @@
 };
 
 struct MockVSyncDispatch : scheduler::VSyncDispatch {
-    MOCK_METHOD2(registerCallback,
-                 CallbackToken(const std::function<void(nsecs_t, nsecs_t, nsecs_t)>&, std::string));
-    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
-    MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
-    MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
-    MOCK_CONST_METHOD1(dump, void(std::string&));
+    MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
+    MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
+    MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
+    MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
 };
 
 struct MockTokenManager : frametimeline::TokenManager {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 98746bc..2bafabd 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1986,6 +1986,38 @@
     }
 }
 
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
+    const DisplayModes displayModes = {
+            createDisplayMode(DisplayModeId(0), 0, (43_Hz).getPeriodNsecs()),
+            createDisplayMode(DisplayModeId(1), 0, (53_Hz).getPeriodNsecs()),
+            createDisplayMode(DisplayModeId(2), 0, (55_Hz).getPeriodNsecs()),
+            createDisplayMode(DisplayModeId(3), 0, (60_Hz).getPeriodNsecs()),
+    };
+
+    const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(displayModes,
+                                                 /*currentConfigId=*/displayModes[0]->getId());
+
+    const auto layers = std::vector<LayerRequirement>{
+            LayerRequirement{
+                    .vote = LayerVoteType::ExplicitDefault,
+                    .desiredRefreshRate = 43_Hz,
+                    .seamlessness = Seamlessness::SeamedAndSeamless,
+                    .weight = 0.41f,
+            },
+            LayerRequirement{
+                    .vote = LayerVoteType::ExplicitExactOrMultiple,
+                    .desiredRefreshRate = 53_Hz,
+                    .seamlessness = Seamlessness::SeamedAndSeamless,
+                    .weight = 0.41f,
+            },
+    };
+
+    EXPECT_EQ(53_Hz, refreshRateConfigs->getBestRefreshRate(layers, globalSignals).getFps());
+}
+
 TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
     EXPECT_TRUE(mExpected60Config < mExpected90Config);
     EXPECT_FALSE(mExpected60Config < mExpected60Config);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
deleted file mode 100644
index 5f6a715..0000000
--- a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#undef LOG_TAG
-#define LOG_TAG "SchedulerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <array>
-
-#include "Scheduler/SchedulerUtils.h"
-
-namespace android {
-namespace scheduler {
-
-class SchedulerUtilsTest : public testing::Test {
-public:
-    SchedulerUtilsTest() = default;
-    ~SchedulerUtilsTest() override = default;
-};
-
-namespace {
-TEST_F(SchedulerUtilsTest, calculate_mean) {
-    std::array<int64_t, 30> testArray{};
-    // Calling the function on empty array returns 0.
-    EXPECT_EQ(0, calculate_mean(testArray));
-
-    testArray[0] = 33;
-    EXPECT_EQ(1, calculate_mean(testArray));
-    testArray[1] = 33;
-    testArray[2] = 33;
-    EXPECT_EQ(3, calculate_mean(testArray));
-    testArray[3] = 42;
-    EXPECT_EQ(4, calculate_mean(testArray));
-    testArray[4] = 33;
-    EXPECT_EQ(5, calculate_mean(testArray));
-    testArray[5] = 42;
-    EXPECT_EQ(7, calculate_mean(testArray));
-    for (int i = 6; i < 30; i++) {
-        testArray[i] = 33;
-    }
-    EXPECT_EQ(33, calculate_mean(testArray));
-}
-
-TEST_F(SchedulerUtilsTest, calculate_median) {
-    std::vector<int64_t> testVector;
-    // Calling the function on empty vector returns 0.
-    EXPECT_EQ(0, calculate_median(&testVector));
-
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculate_median(&testVector));
-    testVector.push_back(33);
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculate_median(&testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculate_median(&testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculate_median(&testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculate_median(&testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculate_median(&testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(42, calculate_median(&testVector));
-    testVector.push_back(60);
-    EXPECT_EQ(42, calculate_median(&testVector));
-    testVector.push_back(60);
-    EXPECT_EQ(42, calculate_median(&testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(42, calculate_median(&testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(42, calculate_median(&testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculate_median(&testVector));
-}
-
-TEST_F(SchedulerUtilsTest, calculate_mode) {
-    std::vector<int64_t> testVector;
-    // Calling the function on empty vector returns 0.
-    EXPECT_EQ(0, calculate_mode(testVector));
-
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(33);
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(33);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(42);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(60);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(60);
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(33);
-    // 5 occurences of 33.
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(42);
-    // 5 occurences of 33, 5 occurences of 42. We choose the first one.
-    EXPECT_EQ(33, calculate_mode(testVector));
-    testVector.push_back(42);
-    // 5 occurences of 33, 6 occurences of 42.
-    EXPECT_EQ(42, calculate_mode(testVector));
-    testVector.push_back(42);
-    // 5 occurences of 33, 7 occurences of 42.
-    EXPECT_EQ(42, calculate_mode(testVector));
-}
-
-} // namespace
-} // namespace scheduler
-} // namespace android
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index fe5f9e0..2b69f13 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -164,8 +164,9 @@
             .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
     EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
     mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                            std::move(eventThread), std::move(sfEventThread), /*callback*/ nullptr,
-                            /*hasMultipleModes*/ true);
+                            std::move(eventThread), std::move(sfEventThread),
+                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
+                            TestableSurfaceFlinger::kTwoDisplayModes);
 }
 
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 56a0506..3205952 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -42,13 +42,18 @@
 
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
 
-        mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
-                           .setSupportedModes({kDisplayMode60, kDisplayMode90, kDisplayMode120,
-                                               kDisplayMode90DifferentResolution})
-                           .setActiveMode(kDisplayModeId60)
-                           .inject();
+        {
+            DisplayModes modes = {kDisplayMode60, kDisplayMode90, kDisplayMode120,
+                                  kDisplayMode90DifferentResolution};
+            const DisplayModeId activeModeId = kDisplayModeId60;
+            auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId);
 
-        setupScheduler();
+            mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+                               .setDisplayModes(modes, activeModeId, std::move(configs))
+                               .inject();
+        }
+
+        setupScheduler(mDisplay->holdRefreshRateConfigs());
 
         // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
         // will call setActiveConfig instead of setActiveConfigWithConstraints.
@@ -57,7 +62,7 @@
     }
 
 protected:
-    void setupScheduler();
+    void setupScheduler(std::shared_ptr<scheduler::RefreshRateConfigs>);
     void testChangeRefreshRate(bool isDisplayActive, bool isRefreshRequired);
 
     sp<DisplayDevice> mDisplay;
@@ -108,7 +113,8 @@
                     .build();
 };
 
-void DisplayModeSwitchingTest::setupScheduler() {
+void DisplayModeSwitchingTest::setupScheduler(
+        std::shared_ptr<scheduler::RefreshRateConfigs> configs) {
     auto eventThread = std::make_unique<mock::EventThread>();
     mAppEventThread = eventThread.get();
     auto sfEventThread = std::make_unique<mock::EventThread>();
@@ -132,8 +138,9 @@
                     Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
     EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
     mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                            std::move(eventThread), std::move(sfEventThread), /*callback*/ nullptr,
-                            /*hasMultipleModes*/ true);
+                            std::move(eventThread), std::move(sfEventThread),
+                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
+                            std::move(configs));
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index b57feff..7948e60 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -61,7 +61,7 @@
 struct EventThreadBaseSupportedVariant {
     static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
         // The callback should not be notified to toggle VSYNC.
-        EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0);
+        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_)).Times(0);
 
         // The event thread should not be notified.
         EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
@@ -88,7 +88,7 @@
 struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
     static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
         // The callback should be notified to enable VSYNC.
-        EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(true)).Times(1);
+        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(true)).Times(1);
 
         // The event thread should be notified that the screen was acquired.
         EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
@@ -96,7 +96,7 @@
 
     static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
         // The callback should be notified to disable VSYNC.
-        EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1);
+        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(false)).Times(1);
 
         // The event thread should not be notified that the screen was released.
         EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 0067997..d292e08 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <algorithm>
+#include <variant>
+
 #include <compositionengine/Display.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
@@ -24,7 +27,6 @@
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/DisplaySurface.h>
 #include <gui/ScreenCaptureResults.h>
-#include <algorithm>
 
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
@@ -45,6 +47,7 @@
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/MockFrameTimeline.h"
 #include "mock/MockFrameTracer.h"
+#include "mock/MockSchedulerCallback.h"
 
 namespace android {
 
@@ -170,7 +173,7 @@
 
 } // namespace surfaceflinger::test
 
-class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback {
+class TestableSurfaceFlinger {
 public:
     using HotplugEvent = SurfaceFlinger::HotplugEvent;
 
@@ -193,42 +196,64 @@
         mFlinger->mCompositionEngine->setTimeStats(timeStats);
     }
 
-    // The ISchedulerCallback argument can be nullptr for a no-op implementation.
+    enum class SchedulerCallbackImpl { kNoOp, kMock };
+
+    static constexpr struct OneDisplayMode {
+    } kOneDisplayMode;
+
+    static constexpr struct TwoDisplayModes {
+    } kTwoDisplayModes;
+
+    using RefreshRateConfigsPtr = std::shared_ptr<scheduler::RefreshRateConfigs>;
+
+    using DisplayModesVariant =
+            std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateConfigsPtr>;
+
     void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
                         std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
-                        scheduler::ISchedulerCallback* callback = nullptr,
-                        bool hasMultipleModes = false) {
-        DisplayModes modes{DisplayMode::Builder(0)
-                                   .setId(DisplayModeId(0))
-                                   .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                   .setVsyncPeriod(16'666'667)
-                                   .setGroup(0)
-                                   .build()};
+                        SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
+                        DisplayModesVariant modesVariant = kOneDisplayMode) {
+        RefreshRateConfigsPtr configs;
+        if (std::holds_alternative<RefreshRateConfigsPtr>(modesVariant)) {
+            configs = std::move(std::get<RefreshRateConfigsPtr>(modesVariant));
+        } else {
+            DisplayModes modes = {DisplayMode::Builder(0)
+                                          .setId(DisplayModeId(0))
+                                          .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+                                          .setVsyncPeriod(16'666'667)
+                                          .setGroup(0)
+                                          .build()};
 
-        if (hasMultipleModes) {
-            modes.emplace_back(DisplayMode::Builder(1)
-                                       .setId(DisplayModeId(1))
-                                       .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                       .setVsyncPeriod(11'111'111)
-                                       .setGroup(0)
-                                       .build());
+            if (std::holds_alternative<TwoDisplayModes>(modesVariant)) {
+                modes.emplace_back(DisplayMode::Builder(1)
+                                           .setId(DisplayModeId(1))
+                                           .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+                                           .setVsyncPeriod(11'111'111)
+                                           .setGroup(0)
+                                           .build());
+            }
+
+            configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, DisplayModeId(0));
         }
 
-        const auto currMode = DisplayModeId(0);
-        mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, currMode);
-        const auto currFps = mRefreshRateConfigs->getCurrentRefreshRate().getFps();
+        const auto currFps = configs->getCurrentRefreshRate().getFps();
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
-                                                              /*powerMode=*/hal::PowerMode::OFF);
+                                                              hal::PowerMode::OFF);
+
+        using Callback = scheduler::ISchedulerCallback;
+        Callback& callback = callbackImpl == SchedulerCallbackImpl::kNoOp
+                ? static_cast<Callback&>(mNoOpSchedulerCallback)
+                : static_cast<Callback&>(mSchedulerCallback);
 
         mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
-                                                      std::move(vsyncTracker), mRefreshRateConfigs,
-                                                      *(callback ?: this));
+                                                      std::move(vsyncTracker), std::move(configs),
+                                                      callback);
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -237,7 +262,8 @@
 
     void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
 
-    scheduler::TestableScheduler& mutableScheduler() const { return *mScheduler; }
+    scheduler::TestableScheduler& mutableScheduler() { return *mScheduler; }
+    scheduler::mock::SchedulerCallback& mockSchedulerCallback() { return mSchedulerCallback; }
 
     using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
@@ -347,6 +373,11 @@
 
     auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
 
+    auto setDisplayBrightness(const sp<IBinder>& display,
+                              const gui::DisplayBrightness& brightness) {
+        return mFlinger->setDisplayBrightness(display, brightness);
+    }
+
     // Allow reading display state without locking, as if called on the SF main thread.
     auto setPowerModeInternal(const sp<DisplayDevice>& display,
                               hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
@@ -657,23 +688,6 @@
                 mHwcDisplayId(hwcDisplayId) {
             mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
-
-            mCreationArgs.activeModeId = DisplayModeId(0);
-            DisplayModePtr activeMode =
-                    DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
-                            .setId(mCreationArgs.activeModeId)
-                            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                            .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
-                            .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
-                            .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
-                            .setDpiX(FakeHwcDisplayInjector::DEFAULT_DPI)
-                            .setDpiY(FakeHwcDisplayInjector::DEFAULT_DPI)
-                            .setGroup(0)
-                            .build();
-
-            DisplayModes modes{activeMode};
-            mCreationArgs.supportedModes = modes;
-            mCreationArgs.refreshRateConfigs = flinger.mRefreshRateConfigs;
         }
 
         sp<IBinder> token() const { return mDisplayToken; }
@@ -696,13 +710,16 @@
 
         auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; }
 
-        auto& setActiveMode(DisplayModeId mode) {
-            mCreationArgs.activeModeId = mode;
-            return *this;
-        }
-
-        auto& setSupportedModes(DisplayModes mode) {
-            mCreationArgs.supportedModes = mode;
+        // If `configs` is nullptr, the injector creates RefreshRateConfigs from the `modes`.
+        // Otherwise, it uses `configs`, which the caller must create using the same `modes`.
+        //
+        // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateConfigs, remove
+        // the `configs` parameter in favor of an alternative setRefreshRateConfigs API.
+        auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId,
+                              std::shared_ptr<scheduler::RefreshRateConfigs> configs = nullptr) {
+            mCreationArgs.supportedModes = std::move(modes);
+            mCreationArgs.activeModeId = activeModeId;
+            mCreationArgs.refreshRateConfigs = std::move(configs);
             return *this;
         }
 
@@ -744,39 +761,58 @@
         }
 
         sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
-            const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
+            auto& modes = mCreationArgs.supportedModes;
+            auto& activeModeId = mCreationArgs.activeModeId;
+
+            if (!mCreationArgs.refreshRateConfigs) {
+                if (modes.empty()) {
+                    activeModeId = DisplayModeId(0);
+                    modes.emplace_back(
+                            DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
+                                    .setId(activeModeId)
+                                    .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+                                    .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
+                                    .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
+                                    .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
+                                    .setDpiX(FakeHwcDisplayInjector::DEFAULT_DPI)
+                                    .setDpiY(FakeHwcDisplayInjector::DEFAULT_DPI)
+                                    .setGroup(0)
+                                    .build());
+                }
+
+                mCreationArgs.refreshRateConfigs =
+                        std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId);
+            }
 
             DisplayDeviceState state;
             if (const auto type = mCreationArgs.connectionType) {
+                const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
                 LOG_ALWAYS_FATAL_IF(!displayId);
                 const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
                 LOG_ALWAYS_FATAL_IF(!physicalId);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
 
-                const DisplayModePtr activeModePtr =
-                        *std::find_if(mCreationArgs.supportedModes.begin(),
-                                      mCreationArgs.supportedModes.end(), [&](DisplayModePtr mode) {
-                                          return mode->getId() == mCreationArgs.activeModeId;
-                                      });
+                const auto it = std::find_if(modes.begin(), modes.end(),
+                                             [&activeModeId](const DisplayModePtr& mode) {
+                                                 return mode->getId() == activeModeId;
+                                             });
+                LOG_ALWAYS_FATAL_IF(it == modes.end());
+
                 state.physical = {.id = *physicalId,
                                   .type = *type,
                                   .hwcDisplayId = *mHwcDisplayId,
                                   .deviceProductInfo = {},
-                                  .supportedModes = mCreationArgs.supportedModes,
-                                  .activeMode = activeModePtr};
+                                  .supportedModes = modes,
+                                  .activeMode = *it};
             }
 
             state.isSecure = mCreationArgs.isSecure;
 
-            mCreationArgs.refreshRateConfigs =
-                    std::make_shared<scheduler::RefreshRateConfigs>(mCreationArgs.supportedModes,
-                                                                    mCreationArgs.activeModeId);
-
-            sp<DisplayDevice> device = new DisplayDevice(mCreationArgs);
-            if (!device->isVirtual()) {
-                device->setActiveMode(mCreationArgs.activeModeId);
+            sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
+            if (!display->isVirtual()) {
+                display->setActiveMode(activeModeId);
             }
-            mFlinger.mutableDisplays().emplace(mDisplayToken, device);
+            mFlinger.mutableDisplays().emplace(mDisplayToken, display);
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
             mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
 
@@ -784,7 +820,7 @@
                 mFlinger.mutablePhysicalDisplayTokens()[physical->id] = mDisplayToken;
             }
 
-            return device;
+            return display;
         }
 
     private:
@@ -795,16 +831,12 @@
     };
 
 private:
-    void scheduleComposite(FrameHint) override {}
-    void setVsyncEnabled(bool) override {}
-    void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
-    void kernelTimerChanged(bool) override {}
-    void triggerOnFrameRateOverridesChanged() {}
-
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
+
+    scheduler::mock::SchedulerCallback mSchedulerCallback;
+    scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
     scheduler::TestableScheduler* mScheduler = nullptr;
-    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 16d4b59..1ce0309 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -434,9 +434,10 @@
     static ComposerState createComposerState(int layerId, sp<Fence> fence,
                                              uint32_t stateFlags = layer_state_t::eBufferChanged) {
         ComposerState composer_state;
-        composer_state.state.bufferData.acquireFence = std::move(fence);
+        composer_state.state.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.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
         composer_state.state.flags = stateFlags;
         return composer_state;
     }
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index deeb785..5364630 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -22,6 +22,7 @@
 #include <gui/SurfaceComposerClient.h>
 #include <log/log.h>
 #include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/FakeExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <utils/String8.h>
 
@@ -101,9 +102,8 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
 
         sp<Fence> fence(new Fence());
-        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         int32_t layerId = layer->getSequence();
-        uint64_t bufferId = buffer->getId();
+        uint64_t bufferId = 42;
         uint64_t frameNumber = 5;
         nsecs_t dequeueTime = 10;
         nsecs_t postTime = 20;
@@ -115,13 +115,16 @@
                     traceTimestamp(layerId, bufferId, frameNumber, postTime,
                                    FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
         BufferData bufferData;
-        bufferData.buffer = buffer;
         bufferData.acquireFence = fence;
         bufferData.frameNumber = frameNumber;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, postTime, /*desiredPresentTime*/ 30, false, dequeueTime,
-                         FrameTimelineInfo{});
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, bufferId,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false,
+                         dequeueTime, FrameTimelineInfo{});
 
         commitTransaction(layer.get());
         bool computeVisisbleRegions;
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index 6e00748..271b1c0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -39,6 +39,7 @@
     layer_state_t layer;
     layer.layerId = 6;
     layer.what = std::numeric_limits<uint64_t>::max();
+    layer.what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
     layer.x = 7;
     layer.matrix.dsdx = 15;
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 704340d..5bb4c92 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -22,6 +22,7 @@
 #include <gui/SurfaceComposerClient.h>
 #include <log/log.h>
 #include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/FakeExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <utils/String8.h>
 
@@ -114,14 +115,17 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         BufferData bufferData;
-        bufferData.buffer = buffer;
         bufferData.acquireFence = fence;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0});
         acquireFence->signalForTest(12);
 
@@ -145,14 +149,17 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         BufferData bufferData;
-        bufferData.buffer = buffer1;
         bufferData.acquireFence = fence1;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture1 = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0});
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -160,14 +167,17 @@
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         nsecs_t start = systemTime();
-        bufferData.buffer = buffer2;
         bufferData.acquireFence = fence2;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture2 = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         2ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0});
         nsecs_t end = systemTime();
         acquireFence2->signalForTest(12);
@@ -203,14 +213,17 @@
 
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         BufferData bufferData;
-        bufferData.buffer = buffer;
         bufferData.acquireFence = fence;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0});
         acquireFence->signalForTest(12);
 
@@ -234,14 +247,17 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         BufferData bufferData;
-        bufferData.buffer = buffer;
         bufferData.acquireFence = fence;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0});
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -269,14 +285,17 @@
 
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         BufferData bufferData;
-        bufferData.buffer = buffer;
         bufferData.acquireFence = fence;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 3, /*inputEventId*/ 0});
         EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -310,27 +329,33 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         BufferData bufferData;
-        bufferData.buffer = buffer1;
         bufferData.acquireFence = fence1;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture1 = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0});
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
-        bufferData.buffer = buffer2;
         bufferData.acquireFence = fence2;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture2 = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0});
         acquireFence2->signalForTest(12);
 
@@ -356,14 +381,17 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         BufferData bufferData;
-        bufferData.buffer = buffer1;
         bufferData.acquireFence = fence1;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture1 = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0});
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -371,14 +399,17 @@
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         auto dropStartTime1 = systemTime();
-        bufferData.buffer = buffer2;
         bufferData.acquireFence = fence2;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture2 = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0});
         auto dropEndTime1 = systemTime();
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -387,14 +418,17 @@
 
         sp<Fence> fence3(new Fence());
         auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3);
-        const auto buffer3 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
         auto dropStartTime2 = systemTime();
-        bufferData.buffer = buffer3;
         bufferData.acquireFence = fence3;
         bufferData.frameNumber = 1;
         bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
         bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-        layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture3 = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                         1ULL /* bufferId */,
+                                                         HAL_PIXEL_FORMAT_RGBA_8888,
+                                                         0ULL /*usage*/);
+        layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt,
                          {/*vsyncId*/ 2, /*inputEventId*/ 0});
         auto dropEndTime2 = systemTime();
         acquireFence3->signalForTest(12);
@@ -432,14 +466,17 @@
         std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
         for (int i = 0; i < 10; i += 2) {
             sp<Fence> fence(new Fence());
-            const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
             BufferData bufferData;
-            bufferData.buffer = buffer;
             bufferData.acquireFence = fence;
             bufferData.frameNumber = 1;
             bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
             bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
-            layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+            std::shared_ptr<renderengine::ExternalTexture> externalTexture = std::make_shared<
+                    renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                             1ULL /* bufferId */,
+                                                             HAL_PIXEL_FORMAT_RGBA_8888,
+                                                             0ULL /*usage*/);
+            layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
                              {/*vsyncId*/ 1, /*inputEventId*/ 0});
             layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
                                                                   /*inputEventId*/ 0},
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 43b09fd..39dbb07 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -29,79 +29,32 @@
 class TransactionTracingTest : public testing::Test {
 protected:
     static constexpr size_t SMALL_BUFFER_SIZE = 1024;
-    std::unique_ptr<android::TransactionTracing> mTracing;
-    void SetUp() override { mTracing = std::make_unique<android::TransactionTracing>(); }
+    TransactionTracing mTracing;
 
-    void TearDown() override {
-        mTracing->disable();
-        mTracing.reset();
-    }
+    void flush(int64_t vsyncId) { mTracing.flush(vsyncId); }
+    proto::TransactionTraceFile writeToProto() { return mTracing.writeToProto(); }
 
-    auto getCommittedTransactions() {
-        std::scoped_lock<std::mutex> lock(mTracing->mMainThreadLock);
-        return mTracing->mCommittedTransactions;
-    }
-
-    auto getQueuedTransactions() {
-        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
-        return mTracing->mQueuedTransactions;
-    }
-
-    auto getUsedBufferSize() {
-        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
-        return mTracing->mBuffer->used();
-    }
-
-    auto flush(int64_t vsyncId) { return mTracing->flush(vsyncId); }
-
-    auto bufferFront() {
-        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+    proto::TransactionTraceEntry bufferFront() {
+        std::scoped_lock<std::mutex> lock(mTracing.mTraceLock);
         proto::TransactionTraceEntry entry;
-        entry.ParseFromString(mTracing->mBuffer->front());
+        entry.ParseFromString(mTracing.mBuffer.front());
         return entry;
     }
 
-    bool threadIsJoinable() {
-        std::scoped_lock lock(mTracing->mMainThreadLock);
-        return mTracing->mThread.joinable();
-    }
-
-    proto::TransactionTraceFile writeToProto() { return mTracing->writeToProto(); }
-
-    auto getCreatedLayers() {
-        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
-        return mTracing->mCreatedLayers;
-    }
-
-    auto getStartingStates() {
-        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
-        return mTracing->mStartingStates;
-    }
-
     void queueAndCommitTransaction(int64_t vsyncId) {
         TransactionState transaction;
         transaction.id = static_cast<uint64_t>(vsyncId * 3);
         transaction.originUid = 1;
         transaction.originPid = 2;
-        mTracing->addQueuedTransaction(transaction);
+        mTracing.addQueuedTransaction(transaction);
         std::vector<TransactionState> transactions;
         transactions.emplace_back(transaction);
-        mTracing->addCommittedTransactions(transactions, vsyncId);
+        mTracing.addCommittedTransactions(transactions, vsyncId);
         flush(vsyncId);
     }
 
-    // Test that we clean up the tracing thread and free any memory allocated.
-    void verifyDisabledTracingState() {
-        EXPECT_FALSE(mTracing->isEnabled());
-        EXPECT_FALSE(threadIsJoinable());
-        EXPECT_EQ(getCommittedTransactions().size(), 0u);
-        EXPECT_EQ(getQueuedTransactions().size(), 0u);
-        EXPECT_EQ(getUsedBufferSize(), 0u);
-        EXPECT_EQ(getStartingStates().size(), 0u);
-    }
-
     void verifyEntry(const proto::TransactionTraceEntry& actualProto,
-                     const std::vector<TransactionState> expectedTransactions,
+                     const std::vector<TransactionState>& expectedTransactions,
                      int64_t expectedVsyncId) {
         EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
         EXPECT_EQ(actualProto.transactions().size(),
@@ -113,16 +66,7 @@
     }
 };
 
-TEST_F(TransactionTracingTest, enable) {
-    EXPECT_FALSE(mTracing->isEnabled());
-    mTracing->enable();
-    EXPECT_TRUE(mTracing->isEnabled());
-    mTracing->disable();
-    verifyDisabledTracingState();
-}
-
 TEST_F(TransactionTracingTest, addTransactions) {
-    mTracing->enable();
     std::vector<TransactionState> transactions;
     transactions.reserve(100);
     for (uint64_t i = 0; i < 100; i++) {
@@ -130,7 +74,7 @@
         transaction.id = i;
         transaction.originPid = static_cast<int32_t>(i);
         transactions.emplace_back(transaction);
-        mTracing->addQueuedTransaction(transaction);
+        mTracing.addQueuedTransaction(transaction);
     }
 
     // Split incoming transactions into two and commit them in reverse order to test out of order
@@ -138,37 +82,31 @@
     std::vector<TransactionState> firstTransactionSet =
             std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
     int64_t firstTransactionSetVsyncId = 42;
-    mTracing->addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
+    mTracing.addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
 
     int64_t secondTransactionSetVsyncId = 43;
     std::vector<TransactionState> secondTransactionSet =
             std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
-    mTracing->addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
+    mTracing.addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
     flush(secondTransactionSetVsyncId);
 
     proto::TransactionTraceFile proto = writeToProto();
-    EXPECT_EQ(proto.entry().size(), 3);
-    // skip starting entry
-    verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId);
-    verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId);
-
-    mTracing->disable();
-    verifyDisabledTracingState();
+    EXPECT_EQ(proto.entry().size(), 2);
+    verifyEntry(proto.entry(0), firstTransactionSet, firstTransactionSetVsyncId);
+    verifyEntry(proto.entry(1), secondTransactionSet, secondTransactionSetVsyncId);
 }
 
 class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
 protected:
     void SetUp() override {
-        TransactionTracingTest::SetUp();
-        mTracing->enable();
         // add layers
-        mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+        mTracing.setBufferSize(SMALL_BUFFER_SIZE);
         const sp<IBinder> fakeLayerHandle = new BBinder();
-        mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
-                               123 /* flags */, -1 /* parentId */);
+        mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
+                              123 /* flags */, -1 /* parentId */);
         const sp<IBinder> fakeChildLayerHandle = new BBinder();
-        mTracing->onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
-                               456 /* flags */, mParentLayerId);
+        mTracing.onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
+                              456 /* flags */, mParentLayerId);
 
         // add some layer transaction
         {
@@ -184,12 +122,12 @@
             childState.state.what = layer_state_t::eLayerChanged;
             childState.state.z = 43;
             transaction.states.add(childState);
-            mTracing->addQueuedTransaction(transaction);
+            mTracing.addQueuedTransaction(transaction);
 
             std::vector<TransactionState> transactions;
             transactions.emplace_back(transaction);
             VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
-            mTracing->addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+            mTracing.addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
             flush(VSYNC_ID_FIRST_LAYER_CHANGE);
         }
 
@@ -204,31 +142,25 @@
             layerState.state.z = 41;
             layerState.state.x = 22;
             transaction.states.add(layerState);
-            mTracing->addQueuedTransaction(transaction);
+            mTracing.addQueuedTransaction(transaction);
 
             std::vector<TransactionState> transactions;
             transactions.emplace_back(transaction);
             VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
-            mTracing->addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+            mTracing.addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
             flush(VSYNC_ID_SECOND_LAYER_CHANGE);
         }
 
         // remove child layer
-        mTracing->onLayerRemoved(2);
+        mTracing.onLayerRemoved(2);
         VSYNC_ID_CHILD_LAYER_REMOVED = ++mVsyncId;
         queueAndCommitTransaction(VSYNC_ID_CHILD_LAYER_REMOVED);
 
         // remove layer
-        mTracing->onLayerRemoved(1);
+        mTracing.onLayerRemoved(1);
         queueAndCommitTransaction(++mVsyncId);
     }
 
-    void TearDown() override {
-        mTracing->disable();
-        verifyDisabledTracingState();
-        TransactionTracingTest::TearDown();
-    }
-
     int mParentLayerId = 1;
     int mChildLayerId = 2;
     int64_t mVsyncId = 0;
@@ -298,16 +230,14 @@
 class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
 protected:
     void SetUp() override {
-        TransactionTracingTest::SetUp();
-        mTracing->enable();
         // add layers
-        mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+        mTracing.setBufferSize(SMALL_BUFFER_SIZE);
         const sp<IBinder> fakeLayerHandle = new BBinder();
-        mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
-                               123 /* flags */, -1 /* parentId */);
+        mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
+                              123 /* flags */, -1 /* parentId */);
         const sp<IBinder> fakeMirrorLayerHandle = new BBinder();
-        mTracing->onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
-                                     mLayerId);
+        mTracing.onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
+                                    mLayerId);
 
         // add some layer transaction
         {
@@ -323,21 +253,15 @@
             mirrorState.state.what = layer_state_t::eLayerChanged;
             mirrorState.state.z = 43;
             transaction.states.add(mirrorState);
-            mTracing->addQueuedTransaction(transaction);
+            mTracing.addQueuedTransaction(transaction);
 
             std::vector<TransactionState> transactions;
             transactions.emplace_back(transaction);
-            mTracing->addCommittedTransactions(transactions, ++mVsyncId);
+            mTracing.addCommittedTransactions(transactions, ++mVsyncId);
             flush(mVsyncId);
         }
     }
 
-    void TearDown() override {
-        mTracing->disable();
-        verifyDisabledTracingState();
-        TransactionTracingTest::TearDown();
-    }
-
     int mLayerId = 5;
     int mMirrorLayerId = 55;
     int64_t mVsyncId = 0;
@@ -349,15 +273,13 @@
 TEST_F(TransactionTracingMirrorLayerTest, canAddMirrorLayers) {
     proto::TransactionTraceFile proto = writeToProto();
     // We don't have any starting states since no layer was removed from.
-    EXPECT_EQ(proto.entry().size(), 2);
-    EXPECT_EQ(proto.entry(0).transactions().size(), 0);
-    EXPECT_EQ(proto.entry(0).added_layers().size(), 0);
+    EXPECT_EQ(proto.entry().size(), 1);
 
     // Verify the mirror layer was added
-    EXPECT_EQ(proto.entry(1).transactions().size(), 1);
-    EXPECT_EQ(proto.entry(1).added_layers().size(), 2);
-    EXPECT_EQ(proto.entry(1).added_layers(1).layer_id(), mMirrorLayerId);
-    EXPECT_EQ(proto.entry(1).transactions(0).layer_changes().size(), 2);
-    EXPECT_EQ(proto.entry(1).transactions(0).layer_changes(1).z(), 43);
+    EXPECT_EQ(proto.entry(0).transactions().size(), 1);
+    EXPECT_EQ(proto.entry(0).added_layers().size(), 2);
+    EXPECT_EQ(proto.entry(0).added_layers(1).layer_id(), mMirrorLayerId);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43);
 }
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 42b1993..2da266b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-#include "Scheduler/TimeKeeper.h"
-#include "Scheduler/Timer.h"
-#include "Scheduler/VSyncDispatchTimerQueue.h"
-#include "Scheduler/VSyncTracker.h"
+#include <thread>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <thread>
+
+#include <scheduler/Timer.h>
+
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
 
 using namespace testing;
 using namespace std::literals;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index ddc02bf..b7f968d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -23,16 +23,19 @@
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 #define LOG_NDEBUG 0
 
-#include "Scheduler/TimeKeeper.h"
-#include "Scheduler/VSyncDispatchTimerQueue.h"
-#include "Scheduler/VSyncTracker.h"
+#include <thread>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <thread>
+
+#include <scheduler/TimeKeeper.h>
+
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
 
 using namespace testing;
 using namespace std::literals;
+
 namespace android::scheduler {
 
 class MockVSyncTracker : public VSyncTracker {
@@ -71,10 +74,10 @@
         ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
     }
 
-    MOCK_CONST_METHOD0(now, nsecs_t());
-    MOCK_METHOD2(alarmAt, void(std::function<void()> const&, nsecs_t time));
-    MOCK_METHOD0(alarmCancel, void());
-    MOCK_CONST_METHOD1(dump, void(std::string&));
+    MOCK_METHOD(nsecs_t, now, (), (const));
+    MOCK_METHOD(void, alarmAt, (std::function<void()>, nsecs_t), (override));
+    MOCK_METHOD(void, alarmCancel, (), (override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
 
     void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
         mCallback = callback;
@@ -196,11 +199,14 @@
         class TimeKeeperWrapper : public TimeKeeper {
         public:
             TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
-            void alarmAt(std::function<void()> const& callback, nsecs_t time) final {
-                mControllableClock.alarmAt(callback, time);
-            }
-            void alarmCancel() final { mControllableClock.alarmCancel(); }
+
             nsecs_t now() const final { return mControllableClock.now(); }
+
+            void alarmAt(std::function<void()> callback, nsecs_t time) final {
+                mControllableClock.alarmAt(std::move(callback), time);
+            }
+
+            void alarmCancel() final { mControllableClock.alarmCancel(); }
             void dump(std::string&) const final {}
 
         private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 5826a9b..4eb9055 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -22,19 +22,22 @@
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 #define LOG_NDEBUG 0
 
-#include "Scheduler/TimeKeeper.h"
-#include "Scheduler/VSyncDispatch.h"
-#include "Scheduler/VSyncReactor.h"
-#include "Scheduler/VSyncTracker.h"
+#include <array>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <ui/Fence.h>
 #include <ui/FenceTime.h>
-#include <array>
+
+#include <scheduler/TimeKeeper.h>
+
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncReactor.h"
+#include "Scheduler/VSyncTracker.h"
 
 using namespace testing;
 using namespace std::literals;
+
 namespace android::scheduler {
 
 class MockVSyncTracker : public VSyncTracker {
@@ -65,14 +68,12 @@
     std::shared_ptr<Clock> const mClock;
 };
 
-class MockVSyncDispatch : public VSyncDispatch {
-public:
-    MOCK_METHOD2(registerCallback,
-                 CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
-    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
-    MOCK_METHOD2(schedule, ScheduleResult(CallbackToken, ScheduleTiming));
-    MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
-    MOCK_CONST_METHOD1(dump, void(std::string&));
+struct MockVSyncDispatch : VSyncDispatch {
+    MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
+    MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
+    MOCK_METHOD(ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
+    MOCK_METHOD(CancelResult, cancel, (CallbackToken), (override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
 };
 
 std::shared_ptr<android::FenceTime> generateInvalidFence() {
@@ -497,4 +498,4 @@
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 8c51313..4273401 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -97,7 +97,8 @@
     MOCK_METHOD3(setLayerSurfaceDamage,
                  Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
     MOCK_METHOD3(setLayerBlendMode, Error(Display, Layer, IComposerClient::BlendMode));
-    MOCK_METHOD3(setLayerColor, Error(Display, Layer, const IComposerClient::Color&));
+    MOCK_METHOD3(setLayerColor,
+                 Error(Display, Layer, const aidl::android::hardware::graphics::composer3::Color&));
     MOCK_METHOD3(setLayerCompositionType,
                  Error(Display, Layer, aidl::android::hardware::graphics::composer3::Composition));
     MOCK_METHOD3(setLayerDataspace, Error(Display, Layer, Dataspace));
@@ -120,7 +121,7 @@
                  Error(Display, uint64_t, uint64_t, DisplayedFrameStats*));
     MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
                  Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
-    MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
+    MOCK_METHOD3(setDisplayBrightness, Error(Display, float, const DisplayBrightnessOptions&));
     MOCK_METHOD2(
             getDisplayCapabilities,
             Error(Display,
@@ -134,6 +135,10 @@
                  V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
                              VsyncPeriodChangeTimeline*));
     MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
+    MOCK_METHOD2(getBootDisplayConfigSupport, Error(Display, bool*));
+    MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config));
+    MOCK_METHOD1(clearBootDisplayConfig, Error(Display));
+    MOCK_METHOD2(getPreferredBootDisplayConfig, Error(Display, Config*));
     MOCK_METHOD2(getSupportedContentTypes,
                  V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
     MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
@@ -145,6 +150,8 @@
     MOCK_METHOD3(getClientTargetProperty,
                  Error(Display, IComposerClient::ClientTargetProperty*, float*));
     MOCK_METHOD3(setLayerWhitePointNits, Error(Display, Layer, float));
+    MOCK_METHOD3(setLayerBlockingRegion,
+                 Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 821fc8f..9015944 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -80,11 +80,15 @@
     MOCK_METHOD(hal::Error, presentOrValidate,
                 (nsecs_t, uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *),
                 (override));
-    MOCK_METHOD(std::future<hal::Error>, setDisplayBrightness, (float), (override));
+    MOCK_METHOD(std::future<hal::Error>, setDisplayBrightness,
+                (float, const Hwc2::Composer::DisplayBrightnessOptions &), (override));
     MOCK_METHOD(hal::Error, setActiveConfigWithConstraints,
                 (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
                  hal::VsyncPeriodChangeTimeline *),
                 (override));
+    MOCK_METHOD(hal::Error, setBootDisplayConfig, (hal::HWConfigId), (override));
+    MOCK_METHOD(hal::Error, clearBootDisplayConfig, (), (override));
+    MOCK_METHOD(hal::Error, getPreferredBootDisplayConfig, (hal::HWConfigId *), (const, override));
     MOCK_METHOD(hal::Error, setAutoLowLatencyMode, (bool), (override));
     MOCK_METHOD(hal::Error, getSupportedContentTypes, (std::vector<hal::ContentType> *),
                 (const, override));
@@ -106,7 +110,8 @@
                 (override));
     MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override));
     MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override));
-    MOCK_METHOD(hal::Error, setColor, (hal::Color), (override));
+    MOCK_METHOD(hal::Error, setColor, (aidl::android::hardware::graphics::composer3::Color),
+                (override));
     MOCK_METHOD(hal::Error, setCompositionType,
                 (aidl::android::hardware::graphics::composer3::Composition), (override));
     MOCK_METHOD(hal::Error, setDataspace, (android::ui::Dataspace), (override));
@@ -123,6 +128,7 @@
     MOCK_METHOD(hal::Error, setLayerGenericMetadata,
                 (const std::string &, bool, const std::vector<uint8_t> &), (override));
     MOCK_METHOD(hal::Error, setWhitePointNits, (float whitePointNits), (override));
+    MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override));
 };
 
 } // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index 23b849a..c598cbc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -37,11 +37,10 @@
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
     MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
     MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
-    MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
-                (override));
     MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
                 (override));
     MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
+    MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 8b48e1c..0840a2f 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -34,6 +34,7 @@
     MOCK_METHOD0(createClone, sp<Layer>());
     MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
     MOCK_CONST_METHOD0(getOwnerUid, uid_t());
+    MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 849e308..c90b8ed 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -35,7 +35,7 @@
     void setVsyncEnabled(bool) override {}
     void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
     void kernelTimerChanged(bool) override {}
-    void triggerOnFrameRateOverridesChanged() {}
+    void triggerOnFrameRateOverridesChanged() override {}
 };
 
 } // namespace android::scheduler::mock
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index fa3b260..c335e2a 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1473,7 +1473,7 @@
     if (!EnsureInitialized())
         return VK_ERROR_OUT_OF_HOST_MEMORY;
 
-    *pApiVersion = VK_API_VERSION_1_1;
+    *pApiVersion = VK_API_VERSION_1_3;
     return VK_SUCCESS;
 }
 
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 33401d2..df70bf4 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -179,6 +179,7 @@
     INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
     INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
     INIT_PROC_EXT(KHR_swapchain, false, instance, GetPhysicalDevicePresentRectanglesKHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceToolProperties);
     // clang-format on
 
     return success;
@@ -334,6 +335,9 @@
     INIT_PROC(false, dev, GetBufferMemoryRequirements2);
     INIT_PROC(false, dev, GetImageMemoryRequirements2);
     INIT_PROC(false, dev, GetImageSparseMemoryRequirements2);
+    INIT_PROC(false, dev, GetDeviceBufferMemoryRequirements);
+    INIT_PROC(false, dev, GetDeviceImageMemoryRequirements);
+    INIT_PROC(false, dev, GetDeviceImageSparseMemoryRequirements);
     INIT_PROC(false, dev, CreateSamplerYcbcrConversion);
     INIT_PROC(false, dev, DestroySamplerYcbcrConversion);
     INIT_PROC(false, dev, GetDeviceQueue2);
@@ -352,6 +356,39 @@
     INIT_PROC(false, dev, GetBufferOpaqueCaptureAddress);
     INIT_PROC(false, dev, GetBufferDeviceAddress);
     INIT_PROC(false, dev, GetDeviceMemoryOpaqueCaptureAddress);
+    INIT_PROC(false, dev, CmdSetCullMode);
+    INIT_PROC(false, dev, CmdSetFrontFace);
+    INIT_PROC(false, dev, CmdSetPrimitiveTopology);
+    INIT_PROC(false, dev, CmdSetViewportWithCount);
+    INIT_PROC(false, dev, CmdSetScissorWithCount);
+    INIT_PROC(false, dev, CmdBindVertexBuffers2);
+    INIT_PROC(false, dev, CmdSetDepthTestEnable);
+    INIT_PROC(false, dev, CmdSetDepthWriteEnable);
+    INIT_PROC(false, dev, CmdSetDepthCompareOp);
+    INIT_PROC(false, dev, CmdSetDepthBoundsTestEnable);
+    INIT_PROC(false, dev, CmdSetStencilTestEnable);
+    INIT_PROC(false, dev, CmdSetStencilOp);
+    INIT_PROC(false, dev, CmdSetRasterizerDiscardEnable);
+    INIT_PROC(false, dev, CmdSetDepthBiasEnable);
+    INIT_PROC(false, dev, CmdSetPrimitiveRestartEnable);
+    INIT_PROC(false, dev, CreatePrivateDataSlot);
+    INIT_PROC(false, dev, DestroyPrivateDataSlot);
+    INIT_PROC(false, dev, SetPrivateData);
+    INIT_PROC(false, dev, GetPrivateData);
+    INIT_PROC(false, dev, CmdCopyBuffer2);
+    INIT_PROC(false, dev, CmdCopyImage2);
+    INIT_PROC(false, dev, CmdBlitImage2);
+    INIT_PROC(false, dev, CmdCopyBufferToImage2);
+    INIT_PROC(false, dev, CmdCopyImageToBuffer2);
+    INIT_PROC(false, dev, CmdResolveImage2);
+    INIT_PROC(false, dev, CmdSetEvent2);
+    INIT_PROC(false, dev, CmdResetEvent2);
+    INIT_PROC(false, dev, CmdWaitEvents2);
+    INIT_PROC(false, dev, CmdPipelineBarrier2);
+    INIT_PROC(false, dev, QueueSubmit2);
+    INIT_PROC(false, dev, CmdWriteTimestamp2);
+    INIT_PROC(false, dev, CmdBeginRendering);
+    INIT_PROC(false, dev, CmdEndRendering);
     // clang-format on
 
     return success;
@@ -530,6 +567,9 @@
 VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
 VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
 VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
+VKAPI_ATTR void GetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements);
+VKAPI_ATTR void GetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements);
+VKAPI_ATTR void GetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
 VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion);
 VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
@@ -548,6 +588,40 @@
 VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
 VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
 VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
+VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties);
+VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode);
+VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace);
+VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology);
+VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports);
+VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors);
+VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides);
+VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable);
+VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable);
+VKAPI_ATTR void CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp);
+VKAPI_ATTR void CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable);
+VKAPI_ATTR void CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable);
+VKAPI_ATTR void CmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp);
+VKAPI_ATTR void CmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable);
+VKAPI_ATTR void CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable);
+VKAPI_ATTR void CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable);
+VKAPI_ATTR VkResult CreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot);
+VKAPI_ATTR void DestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult SetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data);
+VKAPI_ATTR void GetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData);
+VKAPI_ATTR void CmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo);
+VKAPI_ATTR void CmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo);
+VKAPI_ATTR void CmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo);
+VKAPI_ATTR void CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo);
+VKAPI_ATTR void CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo);
+VKAPI_ATTR void CmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo);
+VKAPI_ATTR void CmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo);
+VKAPI_ATTR void CmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask);
+VKAPI_ATTR void CmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos);
+VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo);
+VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence);
+VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query);
+VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo);
+VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer);
 
 VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
     return GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
@@ -625,6 +699,7 @@
         "vkGetPhysicalDeviceSurfaceFormatsKHR",
         "vkGetPhysicalDeviceSurfacePresentModesKHR",
         "vkGetPhysicalDeviceSurfaceSupportKHR",
+        "vkGetPhysicalDeviceToolProperties",
         "vkGetPhysicalDeviceToolPropertiesEXT",
         "vkGetPhysicalDeviceVideoCapabilitiesKHR",
         "vkGetPhysicalDeviceVideoFormatPropertiesKHR",
@@ -680,18 +755,25 @@
         { "vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginQuery) },
         { "vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass) },
         { "vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass2) },
+        { "vkCmdBeginRendering", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRendering) },
         { "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets) },
         { "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer) },
         { "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(CmdBindPipeline) },
         { "vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(CmdBindVertexBuffers) },
+        { "vkCmdBindVertexBuffers2", reinterpret_cast<PFN_vkVoidFunction>(CmdBindVertexBuffers2) },
         { "vkCmdBlitImage", reinterpret_cast<PFN_vkVoidFunction>(CmdBlitImage) },
+        { "vkCmdBlitImage2", reinterpret_cast<PFN_vkVoidFunction>(CmdBlitImage2) },
         { "vkCmdClearAttachments", reinterpret_cast<PFN_vkVoidFunction>(CmdClearAttachments) },
         { "vkCmdClearColorImage", reinterpret_cast<PFN_vkVoidFunction>(CmdClearColorImage) },
         { "vkCmdClearDepthStencilImage", reinterpret_cast<PFN_vkVoidFunction>(CmdClearDepthStencilImage) },
         { "vkCmdCopyBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyBuffer) },
+        { "vkCmdCopyBuffer2", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyBuffer2) },
         { "vkCmdCopyBufferToImage", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyBufferToImage) },
+        { "vkCmdCopyBufferToImage2", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyBufferToImage2) },
         { "vkCmdCopyImage", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyImage) },
+        { "vkCmdCopyImage2", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyImage2) },
         { "vkCmdCopyImageToBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyImageToBuffer) },
+        { "vkCmdCopyImageToBuffer2", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyImageToBuffer2) },
         { "vkCmdCopyQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(CmdCopyQueryPoolResults) },
         { "vkCmdDispatch", reinterpret_cast<PFN_vkVoidFunction>(CmdDispatch) },
         { "vkCmdDispatchBase", reinterpret_cast<PFN_vkVoidFunction>(CmdDispatchBase) },
@@ -705,29 +787,50 @@
         { "vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdEndQuery) },
         { "vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass) },
         { "vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass2) },
+        { "vkCmdEndRendering", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRendering) },
         { "vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(CmdExecuteCommands) },
         { "vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdFillBuffer) },
         { "vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass) },
         { "vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass2) },
         { "vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier) },
+        { "vkCmdPipelineBarrier2", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier2) },
         { "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants) },
         { "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent) },
+        { "vkCmdResetEvent2", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent2) },
         { "vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CmdResetQueryPool) },
         { "vkCmdResolveImage", reinterpret_cast<PFN_vkVoidFunction>(CmdResolveImage) },
+        { "vkCmdResolveImage2", reinterpret_cast<PFN_vkVoidFunction>(CmdResolveImage2) },
         { "vkCmdSetBlendConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdSetBlendConstants) },
+        { "vkCmdSetCullMode", reinterpret_cast<PFN_vkVoidFunction>(CmdSetCullMode) },
         { "vkCmdSetDepthBias", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthBias) },
+        { "vkCmdSetDepthBiasEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthBiasEnable) },
         { "vkCmdSetDepthBounds", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthBounds) },
+        { "vkCmdSetDepthBoundsTestEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthBoundsTestEnable) },
+        { "vkCmdSetDepthCompareOp", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthCompareOp) },
+        { "vkCmdSetDepthTestEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthTestEnable) },
+        { "vkCmdSetDepthWriteEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDepthWriteEnable) },
         { "vkCmdSetDeviceMask", reinterpret_cast<PFN_vkVoidFunction>(CmdSetDeviceMask) },
         { "vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdSetEvent) },
+        { "vkCmdSetEvent2", reinterpret_cast<PFN_vkVoidFunction>(CmdSetEvent2) },
+        { "vkCmdSetFrontFace", reinterpret_cast<PFN_vkVoidFunction>(CmdSetFrontFace) },
         { "vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(CmdSetLineWidth) },
+        { "vkCmdSetPrimitiveRestartEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetPrimitiveRestartEnable) },
+        { "vkCmdSetPrimitiveTopology", reinterpret_cast<PFN_vkVoidFunction>(CmdSetPrimitiveTopology) },
+        { "vkCmdSetRasterizerDiscardEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetRasterizerDiscardEnable) },
         { "vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(CmdSetScissor) },
+        { "vkCmdSetScissorWithCount", reinterpret_cast<PFN_vkVoidFunction>(CmdSetScissorWithCount) },
         { "vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilCompareMask) },
+        { "vkCmdSetStencilOp", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilOp) },
         { "vkCmdSetStencilReference", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilReference) },
+        { "vkCmdSetStencilTestEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilTestEnable) },
         { "vkCmdSetStencilWriteMask", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilWriteMask) },
         { "vkCmdSetViewport", reinterpret_cast<PFN_vkVoidFunction>(CmdSetViewport) },
+        { "vkCmdSetViewportWithCount", reinterpret_cast<PFN_vkVoidFunction>(CmdSetViewportWithCount) },
         { "vkCmdUpdateBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdUpdateBuffer) },
         { "vkCmdWaitEvents", reinterpret_cast<PFN_vkVoidFunction>(CmdWaitEvents) },
+        { "vkCmdWaitEvents2", reinterpret_cast<PFN_vkVoidFunction>(CmdWaitEvents2) },
         { "vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(CmdWriteTimestamp) },
+        { "vkCmdWriteTimestamp2", reinterpret_cast<PFN_vkVoidFunction>(CmdWriteTimestamp2) },
         { "vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(CreateBuffer) },
         { "vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(CreateBufferView) },
         { "vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(CreateCommandPool) },
@@ -745,6 +848,7 @@
         { "vkCreateInstance", nullptr },
         { "vkCreatePipelineCache", reinterpret_cast<PFN_vkVoidFunction>(CreatePipelineCache) },
         { "vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(CreatePipelineLayout) },
+        { "vkCreatePrivateDataSlot", reinterpret_cast<PFN_vkVoidFunction>(CreatePrivateDataSlot) },
         { "vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CreateQueryPool) },
         { "vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass) },
         { "vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass2) },
@@ -769,6 +873,7 @@
         { "vkDestroyPipeline", reinterpret_cast<PFN_vkVoidFunction>(DestroyPipeline) },
         { "vkDestroyPipelineCache", reinterpret_cast<PFN_vkVoidFunction>(DestroyPipelineCache) },
         { "vkDestroyPipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(DestroyPipelineLayout) },
+        { "vkDestroyPrivateDataSlot", reinterpret_cast<PFN_vkVoidFunction>(DestroyPrivateDataSlot) },
         { "vkDestroyQueryPool", reinterpret_cast<PFN_vkVoidFunction>(DestroyQueryPool) },
         { "vkDestroyRenderPass", reinterpret_cast<PFN_vkVoidFunction>(DestroyRenderPass) },
         { "vkDestroySampler", reinterpret_cast<PFN_vkVoidFunction>(DestroySampler) },
@@ -793,9 +898,12 @@
         { "vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements2) },
         { "vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferOpaqueCaptureAddress) },
         { "vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(GetDescriptorSetLayoutSupport) },
+        { "vkGetDeviceBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceBufferMemoryRequirements) },
         { "vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPeerMemoryFeatures) },
         { "vkGetDeviceGroupPresentCapabilitiesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPresentCapabilitiesKHR) },
         { "vkGetDeviceGroupSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupSurfacePresentModesKHR) },
+        { "vkGetDeviceImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageMemoryRequirements) },
+        { "vkGetDeviceImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageSparseMemoryRequirements) },
         { "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryCommitment) },
         { "vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryOpaqueCaptureAddress) },
         { "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr) },
@@ -811,6 +919,7 @@
         { "vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr) },
         { "vkGetMemoryAndroidHardwareBufferANDROID", reinterpret_cast<PFN_vkVoidFunction>(GetMemoryAndroidHardwareBufferANDROID) },
         { "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(GetPipelineCacheData) },
+        { "vkGetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(GetPrivateData) },
         { "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(GetQueryPoolResults) },
         { "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderAreaGranularity) },
         { "vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(GetSemaphoreCounterValue) },
@@ -821,6 +930,7 @@
         { "vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(QueueBindSparse) },
         { "vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR) },
         { "vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit) },
+        { "vkQueueSubmit2", reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit2) },
         { "vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(QueueWaitIdle) },
         { "vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(ResetCommandBuffer) },
         { "vkResetCommandPool", reinterpret_cast<PFN_vkVoidFunction>(ResetCommandPool) },
@@ -829,6 +939,7 @@
         { "vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(ResetFences) },
         { "vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(ResetQueryPool) },
         { "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(SetEvent) },
+        { "vkSetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(SetPrivateData) },
         { "vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(SignalSemaphore) },
         { "vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(TrimCommandPool) },
         { "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory) },
@@ -1515,6 +1626,18 @@
     GetData(device).dispatch.GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
 }
 
+VKAPI_ATTR void GetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+    GetData(device).dispatch.GetDeviceBufferMemoryRequirements(device, pInfo, pMemoryRequirements);
+}
+
+VKAPI_ATTR void GetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+    GetData(device).dispatch.GetDeviceImageMemoryRequirements(device, pInfo, pMemoryRequirements);
+}
+
+VKAPI_ATTR void GetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
+    GetData(device).dispatch.GetDeviceImageSparseMemoryRequirements(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
+}
+
 VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) {
     return GetData(device).dispatch.CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion);
 }
@@ -1587,6 +1710,142 @@
     return GetData(device).dispatch.GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
 }
 
+VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) {
+    return GetData(physicalDevice).dispatch.GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties);
+}
+
+VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) {
+    GetData(commandBuffer).dispatch.CmdSetCullMode(commandBuffer, cullMode);
+}
+
+VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace) {
+    GetData(commandBuffer).dispatch.CmdSetFrontFace(commandBuffer, frontFace);
+}
+
+VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology) {
+    GetData(commandBuffer).dispatch.CmdSetPrimitiveTopology(commandBuffer, primitiveTopology);
+}
+
+VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports) {
+    GetData(commandBuffer).dispatch.CmdSetViewportWithCount(commandBuffer, viewportCount, pViewports);
+}
+
+VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors) {
+    GetData(commandBuffer).dispatch.CmdSetScissorWithCount(commandBuffer, scissorCount, pScissors);
+}
+
+VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
+    GetData(commandBuffer).dispatch.CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides);
+}
+
+VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) {
+    GetData(commandBuffer).dispatch.CmdSetDepthTestEnable(commandBuffer, depthTestEnable);
+}
+
+VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) {
+    GetData(commandBuffer).dispatch.CmdSetDepthWriteEnable(commandBuffer, depthWriteEnable);
+}
+
+VKAPI_ATTR void CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) {
+    GetData(commandBuffer).dispatch.CmdSetDepthCompareOp(commandBuffer, depthCompareOp);
+}
+
+VKAPI_ATTR void CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable) {
+    GetData(commandBuffer).dispatch.CmdSetDepthBoundsTestEnable(commandBuffer, depthBoundsTestEnable);
+}
+
+VKAPI_ATTR void CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) {
+    GetData(commandBuffer).dispatch.CmdSetStencilTestEnable(commandBuffer, stencilTestEnable);
+}
+
+VKAPI_ATTR void CmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) {
+    GetData(commandBuffer).dispatch.CmdSetStencilOp(commandBuffer, faceMask, failOp, passOp, depthFailOp, compareOp);
+}
+
+VKAPI_ATTR void CmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable) {
+    GetData(commandBuffer).dispatch.CmdSetRasterizerDiscardEnable(commandBuffer, rasterizerDiscardEnable);
+}
+
+VKAPI_ATTR void CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) {
+    GetData(commandBuffer).dispatch.CmdSetDepthBiasEnable(commandBuffer, depthBiasEnable);
+}
+
+VKAPI_ATTR void CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable) {
+    GetData(commandBuffer).dispatch.CmdSetPrimitiveRestartEnable(commandBuffer, primitiveRestartEnable);
+}
+
+VKAPI_ATTR VkResult CreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot) {
+    return GetData(device).dispatch.CreatePrivateDataSlot(device, pCreateInfo, pAllocator, pPrivateDataSlot);
+}
+
+VKAPI_ATTR void DestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator) {
+    GetData(device).dispatch.DestroyPrivateDataSlot(device, privateDataSlot, pAllocator);
+}
+
+VKAPI_ATTR VkResult SetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data) {
+    return GetData(device).dispatch.SetPrivateData(device, objectType, objectHandle, privateDataSlot, data);
+}
+
+VKAPI_ATTR void GetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData) {
+    GetData(device).dispatch.GetPrivateData(device, objectType, objectHandle, privateDataSlot, pData);
+}
+
+VKAPI_ATTR void CmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo) {
+    GetData(commandBuffer).dispatch.CmdCopyBuffer2(commandBuffer, pCopyBufferInfo);
+}
+
+VKAPI_ATTR void CmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo) {
+    GetData(commandBuffer).dispatch.CmdCopyImage2(commandBuffer, pCopyImageInfo);
+}
+
+VKAPI_ATTR void CmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo) {
+    GetData(commandBuffer).dispatch.CmdBlitImage2(commandBuffer, pBlitImageInfo);
+}
+
+VKAPI_ATTR void CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) {
+    GetData(commandBuffer).dispatch.CmdCopyBufferToImage2(commandBuffer, pCopyBufferToImageInfo);
+}
+
+VKAPI_ATTR void CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {
+    GetData(commandBuffer).dispatch.CmdCopyImageToBuffer2(commandBuffer, pCopyImageToBufferInfo);
+}
+
+VKAPI_ATTR void CmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo) {
+    GetData(commandBuffer).dispatch.CmdResolveImage2(commandBuffer, pResolveImageInfo);
+}
+
+VKAPI_ATTR void CmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo) {
+    GetData(commandBuffer).dispatch.CmdSetEvent2(commandBuffer, event, pDependencyInfo);
+}
+
+VKAPI_ATTR void CmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask) {
+    GetData(commandBuffer).dispatch.CmdResetEvent2(commandBuffer, event, stageMask);
+}
+
+VKAPI_ATTR void CmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos) {
+    GetData(commandBuffer).dispatch.CmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos);
+}
+
+VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo) {
+    GetData(commandBuffer).dispatch.CmdPipelineBarrier2(commandBuffer, pDependencyInfo);
+}
+
+VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) {
+    return GetData(queue).dispatch.QueueSubmit2(queue, submitCount, pSubmits, fence);
+}
+
+VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) {
+    GetData(commandBuffer).dispatch.CmdWriteTimestamp2(commandBuffer, stage, queryPool, query);
+}
+
+VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) {
+    GetData(commandBuffer).dispatch.CmdBeginRendering(commandBuffer, pRenderingInfo);
+}
+
+VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer) {
+    GetData(commandBuffer).dispatch.CmdEndRendering(commandBuffer);
+}
+
 
 }  // anonymous namespace
 
@@ -2483,6 +2742,21 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+    vulkan::api::GetDeviceBufferMemoryRequirements(device, pInfo, pMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+    vulkan::api::GetDeviceImageMemoryRequirements(device, pInfo, pMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
+    vulkan::api::GetDeviceImageSparseMemoryRequirements(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkCreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) {
     return vulkan::api::CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion);
 }
@@ -2572,4 +2846,174 @@
     return vulkan::api::GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
 }
 
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) {
+    return vulkan::api::GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) {
+    vulkan::api::CmdSetCullMode(commandBuffer, cullMode);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace) {
+    vulkan::api::CmdSetFrontFace(commandBuffer, frontFace);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology) {
+    vulkan::api::CmdSetPrimitiveTopology(commandBuffer, primitiveTopology);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports) {
+    vulkan::api::CmdSetViewportWithCount(commandBuffer, viewportCount, pViewports);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors) {
+    vulkan::api::CmdSetScissorWithCount(commandBuffer, scissorCount, pScissors);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
+    vulkan::api::CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) {
+    vulkan::api::CmdSetDepthTestEnable(commandBuffer, depthTestEnable);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) {
+    vulkan::api::CmdSetDepthWriteEnable(commandBuffer, depthWriteEnable);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) {
+    vulkan::api::CmdSetDepthCompareOp(commandBuffer, depthCompareOp);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable) {
+    vulkan::api::CmdSetDepthBoundsTestEnable(commandBuffer, depthBoundsTestEnable);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) {
+    vulkan::api::CmdSetStencilTestEnable(commandBuffer, stencilTestEnable);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) {
+    vulkan::api::CmdSetStencilOp(commandBuffer, faceMask, failOp, passOp, depthFailOp, compareOp);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable) {
+    vulkan::api::CmdSetRasterizerDiscardEnable(commandBuffer, rasterizerDiscardEnable);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) {
+    vulkan::api::CmdSetDepthBiasEnable(commandBuffer, depthBiasEnable);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable) {
+    vulkan::api::CmdSetPrimitiveRestartEnable(commandBuffer, primitiveRestartEnable);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot) {
+    return vulkan::api::CreatePrivateDataSlot(device, pCreateInfo, pAllocator, pPrivateDataSlot);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::DestroyPrivateDataSlot(device, privateDataSlot, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkSetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data) {
+    return vulkan::api::SetPrivateData(device, objectType, objectHandle, privateDataSlot, data);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData) {
+    vulkan::api::GetPrivateData(device, objectType, objectHandle, privateDataSlot, pData);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo) {
+    vulkan::api::CmdCopyBuffer2(commandBuffer, pCopyBufferInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo) {
+    vulkan::api::CmdCopyImage2(commandBuffer, pCopyImageInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo) {
+    vulkan::api::CmdBlitImage2(commandBuffer, pBlitImageInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) {
+    vulkan::api::CmdCopyBufferToImage2(commandBuffer, pCopyBufferToImageInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {
+    vulkan::api::CmdCopyImageToBuffer2(commandBuffer, pCopyImageToBufferInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo) {
+    vulkan::api::CmdResolveImage2(commandBuffer, pResolveImageInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo) {
+    vulkan::api::CmdSetEvent2(commandBuffer, event, pDependencyInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask) {
+    vulkan::api::CmdResetEvent2(commandBuffer, event, stageMask);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos) {
+    vulkan::api::CmdWaitEvents2(commandBuffer, eventCount, pEvents, pDependencyInfos);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo) {
+    vulkan::api::CmdPipelineBarrier2(commandBuffer, pDependencyInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) {
+    return vulkan::api::QueueSubmit2(queue, submitCount, pSubmits, fence);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) {
+    vulkan::api::CmdWriteTimestamp2(commandBuffer, stage, queryPool, query);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) {
+    vulkan::api::CmdBeginRendering(commandBuffer, pRenderingInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdEndRendering(VkCommandBuffer commandBuffer) {
+    vulkan::api::CmdEndRendering(commandBuffer);
+}
+
 // clang-format on
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index ad5cc34..4998018 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -60,6 +60,7 @@
     PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
     PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
     PFN_vkGetPhysicalDevicePresentRectanglesKHR GetPhysicalDevicePresentRectanglesKHR;
+    PFN_vkGetPhysicalDeviceToolProperties GetPhysicalDeviceToolProperties;
     // clang-format on
 };
 
@@ -207,6 +208,9 @@
     PFN_vkGetBufferMemoryRequirements2 GetBufferMemoryRequirements2;
     PFN_vkGetImageMemoryRequirements2 GetImageMemoryRequirements2;
     PFN_vkGetImageSparseMemoryRequirements2 GetImageSparseMemoryRequirements2;
+    PFN_vkGetDeviceBufferMemoryRequirements GetDeviceBufferMemoryRequirements;
+    PFN_vkGetDeviceImageMemoryRequirements GetDeviceImageMemoryRequirements;
+    PFN_vkGetDeviceImageSparseMemoryRequirements GetDeviceImageSparseMemoryRequirements;
     PFN_vkCreateSamplerYcbcrConversion CreateSamplerYcbcrConversion;
     PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion;
     PFN_vkGetDeviceQueue2 GetDeviceQueue2;
@@ -225,6 +229,39 @@
     PFN_vkGetBufferOpaqueCaptureAddress GetBufferOpaqueCaptureAddress;
     PFN_vkGetBufferDeviceAddress GetBufferDeviceAddress;
     PFN_vkGetDeviceMemoryOpaqueCaptureAddress GetDeviceMemoryOpaqueCaptureAddress;
+    PFN_vkCmdSetCullMode CmdSetCullMode;
+    PFN_vkCmdSetFrontFace CmdSetFrontFace;
+    PFN_vkCmdSetPrimitiveTopology CmdSetPrimitiveTopology;
+    PFN_vkCmdSetViewportWithCount CmdSetViewportWithCount;
+    PFN_vkCmdSetScissorWithCount CmdSetScissorWithCount;
+    PFN_vkCmdBindVertexBuffers2 CmdBindVertexBuffers2;
+    PFN_vkCmdSetDepthTestEnable CmdSetDepthTestEnable;
+    PFN_vkCmdSetDepthWriteEnable CmdSetDepthWriteEnable;
+    PFN_vkCmdSetDepthCompareOp CmdSetDepthCompareOp;
+    PFN_vkCmdSetDepthBoundsTestEnable CmdSetDepthBoundsTestEnable;
+    PFN_vkCmdSetStencilTestEnable CmdSetStencilTestEnable;
+    PFN_vkCmdSetStencilOp CmdSetStencilOp;
+    PFN_vkCmdSetRasterizerDiscardEnable CmdSetRasterizerDiscardEnable;
+    PFN_vkCmdSetDepthBiasEnable CmdSetDepthBiasEnable;
+    PFN_vkCmdSetPrimitiveRestartEnable CmdSetPrimitiveRestartEnable;
+    PFN_vkCreatePrivateDataSlot CreatePrivateDataSlot;
+    PFN_vkDestroyPrivateDataSlot DestroyPrivateDataSlot;
+    PFN_vkSetPrivateData SetPrivateData;
+    PFN_vkGetPrivateData GetPrivateData;
+    PFN_vkCmdCopyBuffer2 CmdCopyBuffer2;
+    PFN_vkCmdCopyImage2 CmdCopyImage2;
+    PFN_vkCmdBlitImage2 CmdBlitImage2;
+    PFN_vkCmdCopyBufferToImage2 CmdCopyBufferToImage2;
+    PFN_vkCmdCopyImageToBuffer2 CmdCopyImageToBuffer2;
+    PFN_vkCmdResolveImage2 CmdResolveImage2;
+    PFN_vkCmdSetEvent2 CmdSetEvent2;
+    PFN_vkCmdResetEvent2 CmdResetEvent2;
+    PFN_vkCmdWaitEvents2 CmdWaitEvents2;
+    PFN_vkCmdPipelineBarrier2 CmdPipelineBarrier2;
+    PFN_vkQueueSubmit2 QueueSubmit2;
+    PFN_vkCmdWriteTimestamp2 CmdWriteTimestamp2;
+    PFN_vkCmdBeginRendering CmdBeginRendering;
+    PFN_vkCmdEndRendering CmdEndRendering;
     // clang-format on
 };
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index cf774fd..9225062 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -365,7 +365,7 @@
                                      const VkAllocationCallbacks& allocator)
     : is_instance_(true),
       allocator_(allocator),
-      loader_api_version_(VK_API_VERSION_1_1),
+      loader_api_version_(VK_API_VERSION_1_3),
       icd_api_version_(icd_api_version),
       physical_dev_(VK_NULL_HANDLE),
       instance_info_(create_info),
@@ -377,7 +377,7 @@
                                      const VkAllocationCallbacks& allocator)
     : is_instance_(false),
       allocator_(allocator),
-      loader_api_version_(VK_API_VERSION_1_1),
+      loader_api_version_(VK_API_VERSION_1_3),
       icd_api_version_(icd_api_version),
       physical_dev_(physical_dev),
       dev_info_(create_info),
@@ -519,6 +519,14 @@
         is_instance_ ? loader_api_version_
                      : std::min(icd_api_version_, loader_api_version_);
     switch (api_version) {
+        case VK_API_VERSION_1_3:
+            hook_extensions_.set(ProcHook::EXTENSION_CORE_1_3);
+            hal_extensions_.set(ProcHook::EXTENSION_CORE_1_3);
+            [[clang::fallthrough]];
+        case VK_API_VERSION_1_2:
+            hook_extensions_.set(ProcHook::EXTENSION_CORE_1_2);
+            hal_extensions_.set(ProcHook::EXTENSION_CORE_1_2);
+            [[clang::fallthrough]];
         case VK_API_VERSION_1_1:
             hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
             hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
@@ -653,6 +661,7 @@
             case ProcHook::EXTENSION_CORE_1_0:
             case ProcHook::EXTENSION_CORE_1_1:
             case ProcHook::EXTENSION_CORE_1_2:
+            case ProcHook::EXTENSION_CORE_1_3:
             case ProcHook::EXTENSION_COUNT:
                 // Device and meta extensions. If we ever get here it's a bug in
                 // our code. But enumerating them lets us avoid having a default
@@ -707,6 +716,7 @@
             case ProcHook::EXTENSION_CORE_1_0:
             case ProcHook::EXTENSION_CORE_1_1:
             case ProcHook::EXTENSION_CORE_1_2:
+            case ProcHook::EXTENSION_CORE_1_3:
             case ProcHook::EXTENSION_COUNT:
                 // Instance and meta extensions. If we ever get here it's a bug
                 // in our code. But enumerating them lets us avoid having a
@@ -1111,7 +1121,7 @@
         if (result != VK_SUCCESS)
             return result;
 
-        icd_api_version ^= VK_VERSION_PATCH(icd_api_version);
+        icd_api_version ^= VK_API_VERSION_PATCH(icd_api_version);
     }
 
     CreateInfoWrapper wrapper(*pCreateInfo, icd_api_version, data_allocator);
@@ -1195,7 +1205,7 @@
 
     CreateInfoWrapper wrapper(
         physicalDevice, *pCreateInfo,
-        properties.apiVersion ^ VK_VERSION_PATCH(properties.apiVersion),
+        properties.apiVersion ^ VK_API_VERSION_PATCH(properties.apiVersion),
         data_allocator);
     VkResult result = wrapper.Validate();
     if (result != VK_SUCCESS)
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 047e774..819f6b2 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -58,6 +58,7 @@
         EXTENSION_CORE_1_0,
         EXTENSION_CORE_1_1,
         EXTENSION_CORE_1_2,
+        EXTENSION_CORE_1_3,
         EXTENSION_COUNT,
         EXTENSION_UNKNOWN,
     };
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index df97d7f..f49e8f3 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -11,20 +11,27 @@
     vkBindImageMemory;
     vkBindImageMemory2; # introduced=28
     vkCmdBeginQuery;
+    vkCmdBeginRendering; # introduced=33
     vkCmdBeginRenderPass;
     vkCmdBeginRenderPass2; # introduced=31
     vkCmdBindDescriptorSets;
     vkCmdBindIndexBuffer;
     vkCmdBindPipeline;
     vkCmdBindVertexBuffers;
+    vkCmdBindVertexBuffers2; #introduced=33
     vkCmdBlitImage;
+    vkCmdBlitImage2; #introduced=33
     vkCmdClearAttachments;
     vkCmdClearColorImage;
     vkCmdClearDepthStencilImage;
     vkCmdCopyBuffer;
+    vkCmdCopyBuffer2; #introduced=33
     vkCmdCopyBufferToImage;
+    vkCmdCopyBufferToImage2; #introduced=33
     vkCmdCopyImage;
+    vkCmdCopyImage2; #introduced=33
     vkCmdCopyImageToBuffer;
+    vkCmdCopyImageToBuffer2; #introduced=33
     vkCmdCopyQueryPoolResults;
     vkCmdDispatch;
     vkCmdDispatchBase; # introduced=28
@@ -36,6 +43,7 @@
     vkCmdDrawIndirect;
     vkCmdDrawIndirectCount; # introduced=31
     vkCmdEndQuery;
+    vkCmdEndRendering; #introduced=33
     vkCmdEndRenderPass;
     vkCmdEndRenderPass2; # introduced=31
     vkCmdExecuteCommands;
@@ -43,24 +51,44 @@
     vkCmdNextSubpass;
     vkCmdNextSubpass2; # introduced=31
     vkCmdPipelineBarrier;
+    vkCmdPipelineBarrier2; #introduced=33
     vkCmdPushConstants;
     vkCmdResetEvent;
+    vkCmdResetEvent2; #introduced=33
     vkCmdResetQueryPool;
     vkCmdResolveImage;
+    vkCmdResolveImage2; #introduced=33
     vkCmdSetBlendConstants;
+    vkCmdSetCullMode; #introduced=33
     vkCmdSetDepthBias;
+    vkCmdSetDepthBiasEnable; #introduced=33
     vkCmdSetDepthBounds;
+    vkCmdSetDepthBoundsTestEnable; #introduced=33
+    vkCmdSetDepthCompareOp; #introduced=33
+    vkCmdSetDepthTestEnable; #introduced=33
+    vkCmdSetDepthWriteEnable; #introduced=33
     vkCmdSetDeviceMask; # introduced=28
     vkCmdSetEvent;
+    vkCmdSetEvent2; #introduced=33
+    vkCmdSetFrontFace; #introduced=33
     vkCmdSetLineWidth;
+    vkCmdSetPrimitiveRestartEnable; #introduced=33
+    vkCmdSetPrimitiveTopology; #introduced=33
+    vkCmdSetRasterizerDiscardEnable; #introduced=33
     vkCmdSetScissor;
+    vkCmdSetScissorWithCount; #introduced=33
     vkCmdSetStencilCompareMask;
+    vkCmdSetStencilOp; #introduced=33
     vkCmdSetStencilReference;
+    vkCmdSetStencilTestEnable; #introduced=33
     vkCmdSetStencilWriteMask;
     vkCmdSetViewport;
+    vkCmdSetViewportWithCount; #introduced=33
     vkCmdUpdateBuffer;
     vkCmdWaitEvents;
+    vkCmdWaitEvents2; #introduced=33
     vkCmdWriteTimestamp;
+    vkCmdWriteTimestamp2; #introduced=33
     vkCreateAndroidSurfaceKHR;
     vkCreateBuffer;
     vkCreateBufferView;
@@ -79,6 +107,7 @@
     vkCreateInstance;
     vkCreatePipelineCache;
     vkCreatePipelineLayout;
+    vkCreatePrivateDataSlot; #introduced=33
     vkCreateQueryPool;
     vkCreateRenderPass;
     vkCreateRenderPass2; # introduced=31
@@ -103,6 +132,7 @@
     vkDestroyPipeline;
     vkDestroyPipelineCache;
     vkDestroyPipelineLayout;
+    vkDestroyPrivateDataSlot; #introduced=33
     vkDestroyQueryPool;
     vkDestroyRenderPass;
     vkDestroySampler;
@@ -130,9 +160,12 @@
     vkGetBufferMemoryRequirements2; # introduced=28
     vkGetBufferOpaqueCaptureAddress; # introduced=31
     vkGetDescriptorSetLayoutSupport; # introduced=28
+    vkGetDeviceBufferMemoryRequirements; #introduced=33
     vkGetDeviceGroupPeerMemoryFeatures; # introduced=28
     vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28
     vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
+    vkGetDeviceImageMemoryRequirements; #introduced=33
+    vkGetDeviceImageSparseMemoryRequirements; #introduced=33
     vkGetDeviceMemoryCommitment;
     vkGetDeviceMemoryOpaqueCaptureAddress; # introduced=31
     vkGetDeviceProcAddr;
@@ -169,7 +202,9 @@
     vkGetPhysicalDeviceSurfaceFormatsKHR;
     vkGetPhysicalDeviceSurfacePresentModesKHR;
     vkGetPhysicalDeviceSurfaceSupportKHR;
+    vkGetPhysicalDeviceToolProperties; #introduced=33
     vkGetPipelineCacheData;
+    vkGetPrivateData; #introduced=33
     vkGetQueryPoolResults;
     vkGetRenderAreaGranularity;
     vkGetSemaphoreCounterValue; # introduced=31
@@ -180,6 +215,7 @@
     vkQueueBindSparse;
     vkQueuePresentKHR;
     vkQueueSubmit;
+    vkQueueSubmit2; #introduced=33
     vkQueueWaitIdle;
     vkResetCommandBuffer;
     vkResetCommandPool;
@@ -188,6 +224,7 @@
     vkResetFences;
     vkResetQueryPool; # introduced=31
     vkSetEvent;
+    vkSetPrivateData; # introduced=33
     vkSignalSemaphore; # introduced=31
     vkTrimCommandPool; # introduced=28
     vkUnmapMemory;
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index b94233b..3c91150 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -260,7 +260,7 @@
 
 VKAPI_ATTR
 VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
-    *pApiVersion = VK_API_VERSION_1_1;
+    *pApiVersion = VK_API_VERSION_1_3;
     return VK_SUCCESS;
 }
 
@@ -397,8 +397,8 @@
 
 void GetPhysicalDeviceProperties(VkPhysicalDevice,
                                  VkPhysicalDeviceProperties* properties) {
-    properties->apiVersion = VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION);
-    properties->driverVersion = VK_MAKE_VERSION(0, 0, 1);
+    properties->apiVersion = VK_MAKE_API_VERSION(0, 1, 2, VK_HEADER_VERSION);
+    properties->driverVersion = VK_MAKE_API_VERSION(0, 0, 0, 1);
     properties->vendorID = 0;
     properties->deviceID = 0;
     properties->deviceType = VK_PHYSICAL_DEVICE_TYPE_OTHER;
@@ -1625,6 +1625,125 @@
     return 0;
 }
 
+void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) {
+}
+
+void CmdEndRendering(VkCommandBuffer commandBuffer) {
+}
+
+void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
+}
+
+void CmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo) {
+}
+
+void CmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo) {
+}
+
+void CmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo) {
+}
+
+void CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo) {
+}
+
+void CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo) {
+}
+
+void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo) {
+}
+
+void CmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask) {
+}
+
+void CmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo) {
+}
+
+void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode) {
+}
+
+void CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable) {
+}
+
+void CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable) {
+}
+
+void CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp) {
+}
+
+void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable) {
+}
+
+void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable) {
+}
+
+void CmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo) {
+}
+
+void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace) {
+}
+
+void CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable) {
+}
+
+void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology) {
+}
+
+void CmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable) {
+}
+
+void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors) {
+}
+
+void CmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) {
+}
+
+void CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable) {
+}
+
+void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports) {
+}
+
+void CmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos) {
+}
+
+void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query) {
+}
+
+VkResult CreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+void DestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator) {
+}
+
+void GetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+}
+
+void GetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+}
+
+void GetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
+}
+
+VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+void GetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData) {
+}
+
+VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult SetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
 #pragma clang diagnostic pop
 // clang-format on
 
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index edda12c..f6dcf09 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -68,18 +68,25 @@
     {"vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginQuery>(CmdBeginQuery))},
     {"vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass>(CmdBeginRenderPass))},
     {"vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass2>(CmdBeginRenderPass2))},
+    {"vkCmdBeginRendering", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRendering>(CmdBeginRendering))},
     {"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets>(CmdBindDescriptorSets))},
     {"vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer>(CmdBindIndexBuffer))},
     {"vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindPipeline>(CmdBindPipeline))},
     {"vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindVertexBuffers>(CmdBindVertexBuffers))},
+    {"vkCmdBindVertexBuffers2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindVertexBuffers2>(CmdBindVertexBuffers2))},
     {"vkCmdBlitImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBlitImage>(CmdBlitImage))},
+    {"vkCmdBlitImage2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBlitImage2>(CmdBlitImage2))},
     {"vkCmdClearAttachments", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdClearAttachments>(CmdClearAttachments))},
     {"vkCmdClearColorImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdClearColorImage>(CmdClearColorImage))},
     {"vkCmdClearDepthStencilImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdClearDepthStencilImage>(CmdClearDepthStencilImage))},
     {"vkCmdCopyBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyBuffer>(CmdCopyBuffer))},
+    {"vkCmdCopyBuffer2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyBuffer2>(CmdCopyBuffer2))},
     {"vkCmdCopyBufferToImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyBufferToImage>(CmdCopyBufferToImage))},
+    {"vkCmdCopyBufferToImage2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyBufferToImage2>(CmdCopyBufferToImage2))},
     {"vkCmdCopyImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyImage>(CmdCopyImage))},
+    {"vkCmdCopyImage2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyImage2>(CmdCopyImage2))},
     {"vkCmdCopyImageToBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyImageToBuffer>(CmdCopyImageToBuffer))},
+    {"vkCmdCopyImageToBuffer2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyImageToBuffer2>(CmdCopyImageToBuffer2))},
     {"vkCmdCopyQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdCopyQueryPoolResults>(CmdCopyQueryPoolResults))},
     {"vkCmdDispatch", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDispatch>(CmdDispatch))},
     {"vkCmdDispatchBase", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDispatchBase>(CmdDispatchBase))},
@@ -93,29 +100,50 @@
     {"vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndQuery>(CmdEndQuery))},
     {"vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass>(CmdEndRenderPass))},
     {"vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass2>(CmdEndRenderPass2))},
+    {"vkCmdEndRendering", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRendering>(CmdEndRendering))},
     {"vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdExecuteCommands>(CmdExecuteCommands))},
     {"vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdFillBuffer>(CmdFillBuffer))},
     {"vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass>(CmdNextSubpass))},
     {"vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass2>(CmdNextSubpass2))},
     {"vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier>(CmdPipelineBarrier))},
+    {"vkCmdPipelineBarrier2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier2>(CmdPipelineBarrier2))},
     {"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants>(CmdPushConstants))},
     {"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent>(CmdResetEvent))},
+    {"vkCmdResetEvent2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent2>(CmdResetEvent2))},
     {"vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetQueryPool>(CmdResetQueryPool))},
     {"vkCmdResolveImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResolveImage>(CmdResolveImage))},
+    {"vkCmdResolveImage2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResolveImage2>(CmdResolveImage2))},
     {"vkCmdSetBlendConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetBlendConstants>(CmdSetBlendConstants))},
+    {"vkCmdSetCullMode", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetCullMode>(CmdSetCullMode))},
     {"vkCmdSetDepthBias", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthBias>(CmdSetDepthBias))},
+    {"vkCmdSetDepthBiasEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthBiasEnable>(CmdSetDepthBiasEnable))},
     {"vkCmdSetDepthBounds", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthBounds>(CmdSetDepthBounds))},
+    {"vkCmdSetDepthBoundsTestEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthBoundsTestEnable>(CmdSetDepthBoundsTestEnable))},
+    {"vkCmdSetDepthCompareOp", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthCompareOp>(CmdSetDepthCompareOp))},
+    {"vkCmdSetDepthTestEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthTestEnable>(CmdSetDepthTestEnable))},
+    {"vkCmdSetDepthWriteEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDepthWriteEnable>(CmdSetDepthWriteEnable))},
     {"vkCmdSetDeviceMask", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetDeviceMask>(CmdSetDeviceMask))},
     {"vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetEvent>(CmdSetEvent))},
+    {"vkCmdSetEvent2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetEvent2>(CmdSetEvent2))},
+    {"vkCmdSetFrontFace", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetFrontFace>(CmdSetFrontFace))},
     {"vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetLineWidth>(CmdSetLineWidth))},
+    {"vkCmdSetPrimitiveRestartEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetPrimitiveRestartEnable>(CmdSetPrimitiveRestartEnable))},
+    {"vkCmdSetPrimitiveTopology", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetPrimitiveTopology>(CmdSetPrimitiveTopology))},
+    {"vkCmdSetRasterizerDiscardEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetRasterizerDiscardEnable>(CmdSetRasterizerDiscardEnable))},
     {"vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetScissor>(CmdSetScissor))},
+    {"vkCmdSetScissorWithCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetScissorWithCount>(CmdSetScissorWithCount))},
     {"vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilCompareMask>(CmdSetStencilCompareMask))},
+    {"vkCmdSetStencilOp", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilOp>(CmdSetStencilOp))},
     {"vkCmdSetStencilReference", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilReference>(CmdSetStencilReference))},
+    {"vkCmdSetStencilTestEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilTestEnable>(CmdSetStencilTestEnable))},
     {"vkCmdSetStencilWriteMask", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilWriteMask>(CmdSetStencilWriteMask))},
     {"vkCmdSetViewport", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetViewport>(CmdSetViewport))},
+    {"vkCmdSetViewportWithCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetViewportWithCount>(CmdSetViewportWithCount))},
     {"vkCmdUpdateBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdUpdateBuffer>(CmdUpdateBuffer))},
     {"vkCmdWaitEvents", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWaitEvents>(CmdWaitEvents))},
+    {"vkCmdWaitEvents2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWaitEvents2>(CmdWaitEvents2))},
     {"vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWriteTimestamp>(CmdWriteTimestamp))},
+    {"vkCmdWriteTimestamp2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWriteTimestamp2>(CmdWriteTimestamp2))},
     {"vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateBuffer>(CreateBuffer))},
     {"vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateBufferView>(CreateBufferView))},
     {"vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateCommandPool>(CreateCommandPool))},
@@ -134,6 +162,7 @@
     {"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateInstance>(CreateInstance))},
     {"vkCreatePipelineCache", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePipelineCache>(CreatePipelineCache))},
     {"vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePipelineLayout>(CreatePipelineLayout))},
+    {"vkCreatePrivateDataSlot", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePrivateDataSlot>(CreatePrivateDataSlot))},
     {"vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateQueryPool>(CreateQueryPool))},
     {"vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass>(CreateRenderPass))},
     {"vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass2>(CreateRenderPass2))},
@@ -159,6 +188,7 @@
     {"vkDestroyPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyPipeline>(DestroyPipeline))},
     {"vkDestroyPipelineCache", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyPipelineCache>(DestroyPipelineCache))},
     {"vkDestroyPipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyPipelineLayout>(DestroyPipelineLayout))},
+    {"vkDestroyPrivateDataSlot", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyPrivateDataSlot>(DestroyPrivateDataSlot))},
     {"vkDestroyQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyQueryPool>(DestroyQueryPool))},
     {"vkDestroyRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyRenderPass>(DestroyRenderPass))},
     {"vkDestroySampler", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroySampler>(DestroySampler))},
@@ -183,7 +213,10 @@
     {"vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements2>(GetBufferMemoryRequirements2))},
     {"vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferOpaqueCaptureAddress>(GetBufferOpaqueCaptureAddress))},
     {"vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDescriptorSetLayoutSupport>(GetDescriptorSetLayoutSupport))},
+    {"vkGetDeviceBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceBufferMemoryRequirements>(GetDeviceBufferMemoryRequirements))},
     {"vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceGroupPeerMemoryFeatures>(GetDeviceGroupPeerMemoryFeatures))},
+    {"vkGetDeviceImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageMemoryRequirements>(GetDeviceImageMemoryRequirements))},
+    {"vkGetDeviceImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageSparseMemoryRequirements>(GetDeviceImageSparseMemoryRequirements))},
     {"vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceMemoryCommitment))},
     {"vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryOpaqueCaptureAddress>(GetDeviceMemoryOpaqueCaptureAddress))},
     {"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr))},
@@ -221,7 +254,9 @@
     {"vkGetPhysicalDeviceSparseImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(GetPhysicalDeviceSparseImageFormatProperties))},
     {"vkGetPhysicalDeviceSparseImageFormatProperties2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties2>(GetPhysicalDeviceSparseImageFormatProperties2))},
     {"vkGetPhysicalDeviceSparseImageFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR>(GetPhysicalDeviceSparseImageFormatProperties2KHR))},
+    {"vkGetPhysicalDeviceToolProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceToolProperties>(GetPhysicalDeviceToolProperties))},
     {"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
+    {"vkGetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPrivateData>(GetPrivateData))},
     {"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
     {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
     {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
@@ -233,6 +268,7 @@
     {"vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueBindSparse>(QueueBindSparse))},
     {"vkQueueSignalReleaseImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID))},
     {"vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSubmit>(QueueSubmit))},
+    {"vkQueueSubmit2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSubmit2>(QueueSubmit2))},
     {"vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueWaitIdle>(QueueWaitIdle))},
     {"vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetCommandBuffer>(ResetCommandBuffer))},
     {"vkResetCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetCommandPool>(ResetCommandPool))},
@@ -241,6 +277,7 @@
     {"vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetFences>(ResetFences))},
     {"vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetQueryPool>(ResetQueryPool))},
     {"vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetEvent>(SetEvent))},
+    {"vkSetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetPrivateData>(SetPrivateData))},
     {"vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSignalSemaphore>(SignalSemaphore))},
     {"vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTrimCommandPool>(TrimCommandPool))},
     {"vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory>(UnmapMemory))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index e59cae9..3e003e3 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -200,6 +200,9 @@
 VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
 VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
 VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
+VKAPI_ATTR void GetDeviceBufferMemoryRequirements(VkDevice device, const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements);
+VKAPI_ATTR void GetDeviceImageMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements);
+VKAPI_ATTR void GetDeviceImageSparseMemoryRequirements(VkDevice device, const VkDeviceImageMemoryRequirements* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
 VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion);
 VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
@@ -220,6 +223,40 @@
 VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
 VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
 VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
+VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties);
+VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode);
+VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace);
+VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology);
+VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports);
+VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors);
+VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides);
+VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable);
+VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable);
+VKAPI_ATTR void CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp);
+VKAPI_ATTR void CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable);
+VKAPI_ATTR void CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable);
+VKAPI_ATTR void CmdSetStencilOp(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp);
+VKAPI_ATTR void CmdSetRasterizerDiscardEnable(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable);
+VKAPI_ATTR void CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable);
+VKAPI_ATTR void CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable);
+VKAPI_ATTR VkResult CreatePrivateDataSlot(VkDevice device, const VkPrivateDataSlotCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlot* pPrivateDataSlot);
+VKAPI_ATTR void DestroyPrivateDataSlot(VkDevice device, VkPrivateDataSlot privateDataSlot, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult SetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t data);
+VKAPI_ATTR void GetPrivateData(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlot privateDataSlot, uint64_t* pData);
+VKAPI_ATTR void CmdCopyBuffer2(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2* pCopyBufferInfo);
+VKAPI_ATTR void CmdCopyImage2(VkCommandBuffer commandBuffer, const VkCopyImageInfo2* pCopyImageInfo);
+VKAPI_ATTR void CmdBlitImage2(VkCommandBuffer commandBuffer, const VkBlitImageInfo2* pBlitImageInfo);
+VKAPI_ATTR void CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo);
+VKAPI_ATTR void CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2* pCopyImageToBufferInfo);
+VKAPI_ATTR void CmdResolveImage2(VkCommandBuffer commandBuffer, const VkResolveImageInfo2* pResolveImageInfo);
+VKAPI_ATTR void CmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfo* pDependencyInfo);
+VKAPI_ATTR void CmdResetEvent2(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2 stageMask);
+VKAPI_ATTR void CmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfo* pDependencyInfos);
+VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo);
+VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence);
+VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query);
+VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo);
+VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer);
 // clang-format on
 
 }  // namespace null_driver
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 438e5dd..da6b00a 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -387,6 +387,19 @@
   }
 };
 
+template <>
+struct EnumTraits<VkShaderFloatControlsIndependence> {
+  static bool exist(uint32_t e) {
+    switch (e) {
+      case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY:
+      case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL:
+      case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE:
+        return true;
+    }
+    return false;
+  }
+};
+
 // VkSparseImageFormatProperties
 
 template <typename Visitor>
@@ -407,6 +420,7 @@
     visitor->Visit("maxResourceSize", &properties->maxResourceSize);
 }
 
+// clang-format off
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor, VkPhysicalDeviceLimits* limits) {
   return
@@ -605,6 +619,200 @@
 }
 
 template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonCore12* core) {
+  return
+    visitor->Visit("features", &core->features) &&
+    visitor->Visit("properties", &core->properties);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Properties* properties) {
+  return
+    visitor->Visit("driverID", &properties->driverID) &&
+    visitor->Visit("driverName", &properties->driverName) &&
+    visitor->Visit("driverInfo", &properties->driverInfo) &&
+    visitor->Visit("conformanceVersion", &properties->conformanceVersion) &&
+    visitor->Visit("denormBehaviorIndependence", &properties->denormBehaviorIndependence) &&
+    visitor->Visit("roundingModeIndependence", &properties->roundingModeIndependence) &&
+    visitor->Visit("shaderSignedZeroInfNanPreserveFloat16", &properties->shaderSignedZeroInfNanPreserveFloat16) &&
+    visitor->Visit("shaderSignedZeroInfNanPreserveFloat32", &properties->shaderSignedZeroInfNanPreserveFloat32) &&
+    visitor->Visit("shaderSignedZeroInfNanPreserveFloat64", &properties->shaderSignedZeroInfNanPreserveFloat64) &&
+    visitor->Visit("shaderDenormPreserveFloat16", &properties->shaderDenormPreserveFloat16) &&
+    visitor->Visit("shaderDenormPreserveFloat32", &properties->shaderDenormPreserveFloat32) &&
+    visitor->Visit("shaderDenormPreserveFloat64", &properties->shaderDenormPreserveFloat64) &&
+    visitor->Visit("shaderDenormFlushToZeroFloat16", &properties->shaderDenormFlushToZeroFloat16) &&
+    visitor->Visit("shaderDenormFlushToZeroFloat32", &properties->shaderDenormFlushToZeroFloat32) &&
+    visitor->Visit("shaderDenormFlushToZeroFloat64", &properties->shaderDenormFlushToZeroFloat64) &&
+    visitor->Visit("shaderRoundingModeRTEFloat16", &properties->shaderRoundingModeRTEFloat16) &&
+    visitor->Visit("shaderRoundingModeRTEFloat32", &properties->shaderRoundingModeRTEFloat32) &&
+    visitor->Visit("shaderRoundingModeRTEFloat64", &properties->shaderRoundingModeRTEFloat64) &&
+    visitor->Visit("shaderRoundingModeRTZFloat16", &properties->shaderRoundingModeRTZFloat16) &&
+    visitor->Visit("shaderRoundingModeRTZFloat32", &properties->shaderRoundingModeRTZFloat32) &&
+    visitor->Visit("shaderRoundingModeRTZFloat64", &properties->shaderRoundingModeRTZFloat64) &&
+    visitor->Visit("maxUpdateAfterBindDescriptorsInAllPools", &properties->maxUpdateAfterBindDescriptorsInAllPools) &&
+    visitor->Visit("shaderUniformBufferArrayNonUniformIndexingNative", &properties->shaderUniformBufferArrayNonUniformIndexingNative) &&
+    visitor->Visit("shaderSampledImageArrayNonUniformIndexingNative", &properties->shaderSampledImageArrayNonUniformIndexingNative) &&
+    visitor->Visit("shaderStorageBufferArrayNonUniformIndexingNative", &properties->shaderStorageBufferArrayNonUniformIndexingNative) &&
+    visitor->Visit("shaderStorageImageArrayNonUniformIndexingNative", &properties->shaderStorageImageArrayNonUniformIndexingNative) &&
+    visitor->Visit("shaderInputAttachmentArrayNonUniformIndexingNative", &properties->shaderInputAttachmentArrayNonUniformIndexingNative) &&
+    visitor->Visit("robustBufferAccessUpdateAfterBind", &properties->robustBufferAccessUpdateAfterBind) &&
+    visitor->Visit("quadDivergentImplicitLod", &properties->quadDivergentImplicitLod) &&
+    visitor->Visit("maxPerStageDescriptorUpdateAfterBindSamplers", &properties->maxPerStageDescriptorUpdateAfterBindSamplers) &&
+    visitor->Visit("maxPerStageDescriptorUpdateAfterBindUniformBuffers", &properties->maxPerStageDescriptorUpdateAfterBindUniformBuffers) &&
+    visitor->Visit("maxPerStageDescriptorUpdateAfterBindStorageBuffers", &properties->maxPerStageDescriptorUpdateAfterBindStorageBuffers) &&
+    visitor->Visit("maxPerStageDescriptorUpdateAfterBindSampledImages", &properties->maxPerStageDescriptorUpdateAfterBindSampledImages) &&
+    visitor->Visit("maxPerStageDescriptorUpdateAfterBindStorageImages", &properties->maxPerStageDescriptorUpdateAfterBindStorageImages) &&
+    visitor->Visit("maxPerStageDescriptorUpdateAfterBindInputAttachments", &properties->maxPerStageDescriptorUpdateAfterBindInputAttachments) &&
+    visitor->Visit("maxPerStageUpdateAfterBindResources", &properties->maxPerStageUpdateAfterBindResources) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindSamplers", &properties->maxDescriptorSetUpdateAfterBindSamplers) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindUniformBuffers", &properties->maxDescriptorSetUpdateAfterBindUniformBuffers) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindUniformBuffersDynamic", &properties->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindStorageBuffers", &properties->maxDescriptorSetUpdateAfterBindStorageBuffers) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindStorageBuffersDynamic", &properties->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindSampledImages", &properties->maxDescriptorSetUpdateAfterBindSampledImages) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindStorageImages", &properties->maxDescriptorSetUpdateAfterBindStorageImages) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindInputAttachments", &properties->maxDescriptorSetUpdateAfterBindInputAttachments) &&
+    visitor->Visit("supportedDepthResolveModes", &properties->supportedDepthResolveModes) &&
+    visitor->Visit("supportedStencilResolveModes", &properties->supportedStencilResolveModes) &&
+    visitor->Visit("independentResolveNone", &properties->independentResolveNone) &&
+    visitor->Visit("independentResolve", &properties->independentResolve) &&
+    visitor->Visit("filterMinmaxSingleComponentFormats", &properties->filterMinmaxSingleComponentFormats) &&
+    visitor->Visit("filterMinmaxImageComponentMapping", &properties->filterMinmaxImageComponentMapping) &&
+    visitor->Visit("maxTimelineSemaphoreValueDifference", &properties->maxTimelineSemaphoreValueDifference) &&
+    visitor->Visit("framebufferIntegerColorSampleCounts", &properties->framebufferIntegerColorSampleCounts);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan12Features* features) {
+  return
+    visitor->Visit("samplerMirrorClampToEdge", &features->samplerMirrorClampToEdge) &&
+    visitor->Visit("drawIndirectCount", &features->drawIndirectCount) &&
+    visitor->Visit("storageBuffer8BitAccess", &features->storageBuffer8BitAccess) &&
+    visitor->Visit("uniformAndStorageBuffer8BitAccess", &features->uniformAndStorageBuffer8BitAccess) &&
+    visitor->Visit("storagePushConstant8", &features->storagePushConstant8) &&
+    visitor->Visit("shaderBufferInt64Atomics", &features->shaderBufferInt64Atomics) &&
+    visitor->Visit("shaderSharedInt64Atomics", &features->shaderSharedInt64Atomics) &&
+    visitor->Visit("shaderFloat16", &features->shaderFloat16) &&
+    visitor->Visit("shaderInt8", &features->shaderInt8) &&
+    visitor->Visit("descriptorIndexing", &features->descriptorIndexing) &&
+    visitor->Visit("shaderInputAttachmentArrayDynamicIndexing", &features->shaderInputAttachmentArrayDynamicIndexing) &&
+    visitor->Visit("shaderUniformTexelBufferArrayDynamicIndexing", &features->shaderUniformTexelBufferArrayDynamicIndexing) &&
+    visitor->Visit("shaderStorageTexelBufferArrayDynamicIndexing", &features->shaderStorageTexelBufferArrayDynamicIndexing) &&
+    visitor->Visit("shaderUniformBufferArrayNonUniformIndexing", &features->shaderUniformBufferArrayNonUniformIndexing) &&
+    visitor->Visit("shaderSampledImageArrayNonUniformIndexing", &features->shaderSampledImageArrayNonUniformIndexing) &&
+    visitor->Visit("shaderStorageBufferArrayNonUniformIndexing", &features->shaderStorageBufferArrayNonUniformIndexing) &&
+    visitor->Visit("shaderStorageImageArrayNonUniformIndexing", &features->shaderStorageImageArrayNonUniformIndexing) &&
+    visitor->Visit("shaderInputAttachmentArrayNonUniformIndexing", &features->shaderInputAttachmentArrayNonUniformIndexing) &&
+    visitor->Visit("shaderUniformTexelBufferArrayNonUniformIndexing", &features->shaderUniformTexelBufferArrayNonUniformIndexing) &&
+    visitor->Visit("shaderStorageTexelBufferArrayNonUniformIndexing", &features->shaderStorageTexelBufferArrayNonUniformIndexing) &&
+    visitor->Visit("descriptorBindingUniformBufferUpdateAfterBind", &features->descriptorBindingUniformBufferUpdateAfterBind) &&
+    visitor->Visit("descriptorBindingSampledImageUpdateAfterBind", &features->descriptorBindingSampledImageUpdateAfterBind) &&
+    visitor->Visit("descriptorBindingStorageImageUpdateAfterBind", &features->descriptorBindingStorageImageUpdateAfterBind) &&
+    visitor->Visit("descriptorBindingStorageBufferUpdateAfterBind", &features->descriptorBindingStorageBufferUpdateAfterBind) &&
+    visitor->Visit("descriptorBindingUniformTexelBufferUpdateAfterBind", &features->descriptorBindingUniformTexelBufferUpdateAfterBind) &&
+    visitor->Visit("descriptorBindingStorageTexelBufferUpdateAfterBind", &features->descriptorBindingStorageTexelBufferUpdateAfterBind) &&
+    visitor->Visit("descriptorBindingUpdateUnusedWhilePending", &features->descriptorBindingUpdateUnusedWhilePending) &&
+    visitor->Visit("descriptorBindingPartiallyBound", &features->descriptorBindingPartiallyBound) &&
+    visitor->Visit("descriptorBindingVariableDescriptorCount", &features->descriptorBindingVariableDescriptorCount) &&
+    visitor->Visit("runtimeDescriptorArray", &features->runtimeDescriptorArray) &&
+    visitor->Visit("samplerFilterMinmax", &features->samplerFilterMinmax) &&
+    visitor->Visit("scalarBlockLayout", &features->scalarBlockLayout) &&
+    visitor->Visit("imagelessFramebuffer", &features->imagelessFramebuffer) &&
+    visitor->Visit("uniformBufferStandardLayout", &features->uniformBufferStandardLayout) &&
+    visitor->Visit("shaderSubgroupExtendedTypes", &features->shaderSubgroupExtendedTypes) &&
+    visitor->Visit("separateDepthStencilLayouts", &features->separateDepthStencilLayouts) &&
+    visitor->Visit("hostQueryReset", &features->hostQueryReset) &&
+    visitor->Visit("timelineSemaphore", &features->timelineSemaphore) &&
+    visitor->Visit("bufferDeviceAddress", &features->bufferDeviceAddress) &&
+    visitor->Visit("bufferDeviceAddressCaptureReplay", &features->bufferDeviceAddressCaptureReplay) &&
+    visitor->Visit("bufferDeviceAddressMultiDevice", &features->bufferDeviceAddressMultiDevice) &&
+    visitor->Visit("vulkanMemoryModel", &features->vulkanMemoryModel) &&
+    visitor->Visit("vulkanMemoryModelDeviceScope", &features->vulkanMemoryModelDeviceScope) &&
+    visitor->Visit("vulkanMemoryModelAvailabilityVisibilityChains", &features->vulkanMemoryModelAvailabilityVisibilityChains) &&
+    visitor->Visit("shaderOutputViewportIndex", &features->shaderOutputViewportIndex) &&
+    visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) &&
+    visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkJsonCore13* core) {
+  return
+    visitor->Visit("features", &core->features) &&
+    visitor->Visit("properties", &core->properties);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan13Properties* properties) {
+  return
+    visitor->Visit("minSubgroupSize", &properties->minSubgroupSize) &&
+    visitor->Visit("maxSubgroupSize", &properties->maxSubgroupSize) &&
+    visitor->Visit("maxComputeWorkgroupSubgroups", &properties->maxComputeWorkgroupSubgroups) &&
+    visitor->Visit("requiredSubgroupSizeStages", &properties->requiredSubgroupSizeStages) &&
+    visitor->Visit("maxInlineUniformBlockSize", &properties->maxInlineUniformBlockSize) &&
+    visitor->Visit("maxPerStageDescriptorInlineUniformBlocks", &properties->maxPerStageDescriptorInlineUniformBlocks) &&
+    visitor->Visit("maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks", &properties->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks) &&
+    visitor->Visit("maxDescriptorSetInlineUniformBlocks", &properties->maxDescriptorSetInlineUniformBlocks) &&
+    visitor->Visit("maxDescriptorSetUpdateAfterBindInlineUniformBlocks", &properties->maxDescriptorSetUpdateAfterBindInlineUniformBlocks) &&
+    visitor->Visit("maxInlineUniformTotalSize", &properties->maxInlineUniformTotalSize) &&
+    visitor->Visit("integerDotProduct8BitUnsignedAccelerated", &properties->integerDotProduct8BitUnsignedAccelerated) &&
+    visitor->Visit("integerDotProduct8BitSignedAccelerated", &properties->integerDotProduct8BitSignedAccelerated) &&
+    visitor->Visit("integerDotProduct8BitMixedSignednessAccelerated", &properties->integerDotProduct8BitMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProduct4x8BitPackedUnsignedAccelerated", &properties->integerDotProduct4x8BitPackedUnsignedAccelerated) &&
+    visitor->Visit("integerDotProduct4x8BitPackedSignedAccelerated", &properties->integerDotProduct4x8BitPackedSignedAccelerated) &&
+    visitor->Visit("integerDotProduct4x8BitPackedMixedSignednessAccelerated", &properties->integerDotProduct4x8BitPackedMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProduct16BitUnsignedAccelerated", &properties->integerDotProduct16BitUnsignedAccelerated) &&
+    visitor->Visit("integerDotProduct16BitSignedAccelerated", &properties->integerDotProduct16BitSignedAccelerated) &&
+    visitor->Visit("integerDotProduct16BitMixedSignednessAccelerated", &properties->integerDotProduct16BitMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProduct32BitUnsignedAccelerated", &properties->integerDotProduct32BitUnsignedAccelerated) &&
+    visitor->Visit("integerDotProduct32BitSignedAccelerated", &properties->integerDotProduct32BitSignedAccelerated) &&
+    visitor->Visit("integerDotProduct32BitMixedSignednessAccelerated", &properties->integerDotProduct32BitMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProduct64BitUnsignedAccelerated", &properties->integerDotProduct64BitUnsignedAccelerated) &&
+    visitor->Visit("integerDotProduct64BitSignedAccelerated", &properties->integerDotProduct64BitSignedAccelerated) &&
+    visitor->Visit("integerDotProduct64BitMixedSignednessAccelerated", &properties->integerDotProduct64BitMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating8BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating8BitUnsignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating8BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating8BitSignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating16BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating16BitUnsignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating16BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating16BitSignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating32BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating32BitUnsignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating32BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating32BitSignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating64BitUnsignedAccelerated", &properties->integerDotProductAccumulatingSaturating64BitUnsignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating64BitSignedAccelerated", &properties->integerDotProductAccumulatingSaturating64BitSignedAccelerated) &&
+    visitor->Visit("integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated", &properties->integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated) &&
+    visitor->Visit("storageTexelBufferOffsetAlignmentBytes", &properties->storageTexelBufferOffsetAlignmentBytes) &&
+    visitor->Visit("storageTexelBufferOffsetSingleTexelAlignment", &properties->storageTexelBufferOffsetSingleTexelAlignment) &&
+    visitor->Visit("uniformTexelBufferOffsetAlignmentBytes", &properties->uniformTexelBufferOffsetAlignmentBytes) &&
+    visitor->Visit("uniformTexelBufferOffsetSingleTexelAlignment", &properties->uniformTexelBufferOffsetSingleTexelAlignment) &&
+    visitor->Visit("maxBufferSize", &properties->maxBufferSize);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor, VkPhysicalDeviceVulkan13Features* features) {
+  return
+    visitor->Visit("robustImageAccess", &features->robustImageAccess) &&
+    visitor->Visit("inlineUniformBlock", &features->inlineUniformBlock) &&
+    visitor->Visit("descriptorBindingInlineUniformBlockUpdateAfterBind", &features->descriptorBindingInlineUniformBlockUpdateAfterBind) &&
+    visitor->Visit("pipelineCreationCacheControl", &features->pipelineCreationCacheControl) &&
+    visitor->Visit("privateData", &features->privateData) &&
+    visitor->Visit("shaderDemoteToHelperInvocation", &features->shaderDemoteToHelperInvocation) &&
+    visitor->Visit("shaderTerminateInvocation", &features->shaderTerminateInvocation) &&
+    visitor->Visit("subgroupSizeControl", &features->subgroupSizeControl) &&
+    visitor->Visit("computeFullSubgroups", &features->computeFullSubgroups) &&
+    visitor->Visit("synchronization2", &features->synchronization2) &&
+    visitor->Visit("textureCompressionASTC_HDR", &features->textureCompressionASTC_HDR) &&
+    visitor->Visit("shaderZeroInitializeWorkgroupMemory", &features->shaderZeroInitializeWorkgroupMemory) &&
+    visitor->Visit("dynamicRendering", &features->dynamicRendering) &&
+    visitor->Visit("shaderIntegerDotProduct", &features->shaderIntegerDotProduct) &&
+    visitor->Visit("maintenance4", &features->maintenance4);
+}
+// clang-format on
+
+template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
                     VkJsonExtDriverProperties* properties) {
   return visitor->Visit("driverPropertiesKHR",
@@ -841,8 +1049,12 @@
 inline bool Iterate(Visitor* visitor, VkJsonDevice* device) {
   bool ret = true;
   switch (device->properties.apiVersion ^
-          VK_VERSION_PATCH(device->properties.apiVersion)) {
+          VK_API_VERSION_PATCH(device->properties.apiVersion)) {
+    case VK_API_VERSION_1_3:
+      ret &= visitor->Visit("core13", &device->core13);
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_2:
+      ret &= visitor->Visit("core12", &device->core12);
       FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_1:
       ret &=
@@ -897,16 +1109,22 @@
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) {
   bool ret = true;
-  switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) {
+  switch (instance->api_version ^ VK_API_VERSION_PATCH(instance->api_version)) {
+    case VK_API_VERSION_1_3:
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_2:
+      ret &= visitor->Visit("apiVersion", &instance->api_version);
       FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_1:
       ret &= visitor->Visit("deviceGroups", &instance->device_groups);
       FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_0:
+      char depString[] =
+          "vkjson is deprecated, and will be replaced in a future release";
       ret &= visitor->Visit("layers", &instance->layers) &&
              visitor->Visit("extensions", &instance->extensions) &&
-             visitor->Visit("devices", &instance->devices);
+             visitor->Visit("devices", &instance->devices) &&
+             visitor->Visit("_comment", &depString);
   }
   return ret;
 }
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index 52e7bee..88f6e7d 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -33,14 +33,6 @@
 #undef max
 #endif
 
-#ifndef VK_API_VERSION_1_0
-#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)
-#endif
-
-#ifndef VK_API_VERSION_1_1
-#define VK_API_VERSION_1_1 VK_MAKE_VERSION(1, 1, 0)
-#endif
-
 /*
  * Annotation to tell clang that we intend to fall through from one case to
  * another in a switch. Sourced from android-base/macros.h.
@@ -82,6 +74,16 @@
   VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_float16_int8_features_khr;
 };
 
+struct VkJsonCore12 {
+  VkPhysicalDeviceVulkan12Properties properties;
+  VkPhysicalDeviceVulkan12Features features;
+};
+
+struct VkJsonCore13 {
+  VkPhysicalDeviceVulkan13Properties properties;
+  VkPhysicalDeviceVulkan13Features features;
+};
+
 struct VkJsonDevice {
   VkJsonDevice() {
     memset(&properties, 0, sizeof(VkPhysicalDeviceProperties));
@@ -106,6 +108,8 @@
            sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures));
     memset(&shader_draw_parameter_features, 0,
            sizeof(VkPhysicalDeviceShaderDrawParameterFeatures));
+    memset(&core12, 0, sizeof(VkJsonCore12));
+    memset(&core13, 0, sizeof(VkJsonCore13));
   }
   VkPhysicalDeviceProperties properties;
   VkPhysicalDeviceFeatures features;
@@ -133,6 +137,8 @@
       external_fence_properties;
   std::map<VkExternalSemaphoreHandleTypeFlagBits, VkExternalSemaphoreProperties>
       external_semaphore_properties;
+  VkJsonCore12 core12;
+  VkJsonCore13 core13;
 };
 
 struct VkJsonDeviceGroup {
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 5872495..0ffe7e0 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -157,76 +157,64 @@
       }
     }
 
-    VkPhysicalDeviceProperties2 properties2 = {
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
-        nullptr,
-        {},
-    };
-
     device.subgroup_properties.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
-    device.subgroup_properties.pNext = properties2.pNext;
-    properties2.pNext = &device.subgroup_properties;
+    device.subgroup_properties.pNext = properties.pNext;
+    properties.pNext = &device.subgroup_properties;
 
     device.point_clipping_properties.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES;
-    device.point_clipping_properties.pNext = properties2.pNext;
-    properties2.pNext = &device.point_clipping_properties;
+    device.point_clipping_properties.pNext = properties.pNext;
+    properties.pNext = &device.point_clipping_properties;
 
     device.multiview_properties.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
-    device.multiview_properties.pNext = properties2.pNext;
-    properties2.pNext = &device.multiview_properties;
+    device.multiview_properties.pNext = properties.pNext;
+    properties.pNext = &device.multiview_properties;
 
     device.id_properties.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
-    device.id_properties.pNext = properties2.pNext;
-    properties2.pNext = &device.id_properties;
+    device.id_properties.pNext = properties.pNext;
+    properties.pNext = &device.id_properties;
 
     device.maintenance3_properties.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
-    device.maintenance3_properties.pNext = properties2.pNext;
-    properties2.pNext = &device.maintenance3_properties;
+    device.maintenance3_properties.pNext = properties.pNext;
+    properties.pNext = &device.maintenance3_properties;
 
-    vkGetPhysicalDeviceProperties2(physical_device, &properties2);
-
-    VkPhysicalDeviceFeatures2 features2 = {
-        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
-        nullptr,
-        {},
-    };
+    vkGetPhysicalDeviceProperties2(physical_device, &properties);
 
     device.bit16_storage_features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
-    device.bit16_storage_features.pNext = features2.pNext;
-    features2.pNext = &device.bit16_storage_features;
+    device.bit16_storage_features.pNext = features.pNext;
+    features.pNext = &device.bit16_storage_features;
 
     device.multiview_features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
-    device.multiview_features.pNext = features2.pNext;
-    features2.pNext = &device.multiview_features;
+    device.multiview_features.pNext = features.pNext;
+    features.pNext = &device.multiview_features;
 
     device.variable_pointer_features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
-    device.variable_pointer_features.pNext = features2.pNext;
-    features2.pNext = &device.variable_pointer_features;
+    device.variable_pointer_features.pNext = features.pNext;
+    features.pNext = &device.variable_pointer_features;
 
     device.protected_memory_features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
-    device.protected_memory_features.pNext = features2.pNext;
-    features2.pNext = &device.protected_memory_features;
+    device.protected_memory_features.pNext = features.pNext;
+    features.pNext = &device.protected_memory_features;
 
     device.sampler_ycbcr_conversion_features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
-    device.sampler_ycbcr_conversion_features.pNext = features2.pNext;
-    features2.pNext = &device.sampler_ycbcr_conversion_features;
+    device.sampler_ycbcr_conversion_features.pNext = features.pNext;
+    features.pNext = &device.sampler_ycbcr_conversion_features;
 
     device.shader_draw_parameter_features.sType =
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
-    device.shader_draw_parameter_features.pNext = features2.pNext;
-    features2.pNext = &device.shader_draw_parameter_features;
+    device.shader_draw_parameter_features.pNext = features.pNext;
+    features.pNext = &device.shader_draw_parameter_features;
 
-    vkGetPhysicalDeviceFeatures2(physical_device, &features2);
+    vkGetPhysicalDeviceFeatures2(physical_device, &features);
 
     VkPhysicalDeviceExternalFenceInfo external_fence_info = {
         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr,
@@ -272,6 +260,38 @@
     }
   }
 
+  if (device.properties.apiVersion >= VK_API_VERSION_1_2) {
+    device.core12.properties.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES;
+    device.core12.properties.pNext = properties.pNext;
+    properties.pNext = &device.core12.properties;
+
+    vkGetPhysicalDeviceProperties2(physical_device, &properties);
+
+    device.core12.features.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
+    device.core12.features.pNext = features.pNext;
+    features.pNext = &device.core12.features;
+
+    vkGetPhysicalDeviceFeatures2(physical_device, &features);
+  }
+
+  if (device.properties.apiVersion >= VK_API_VERSION_1_3) {
+    device.core13.properties.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES;
+    device.core13.properties.pNext = properties.pNext;
+    properties.pNext = &device.core13.properties;
+
+    vkGetPhysicalDeviceProperties2(physical_device, &properties);
+
+    device.core13.features.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
+    device.core13.features.pNext = features.pNext;
+    features.pNext = &device.core13.features;
+
+    vkGetPhysicalDeviceFeatures2(physical_device, &features);
+  }
+
   return device;
 }